Compare commits

...

6 commits

Author SHA1 Message Date
Howard Abrams
ef61ce1e37 Change garbage collection settings
While I don't mind Emacs taking more memory now, I figured I wouldn't
do any garbage collection during start, and then drop down to a nice
level after.
2025-01-28 13:18:08 -08:00
Howard Abrams
fcb1fdb11d Shuffling my fonts ... yet again. 2025-01-28 13:14:48 -08:00
Howard Abrams
631bdecab1 Brought back my file listing when opening eshell
My life is already a dad joke.

Why didn't I think of putting the directory and git branch on the
header-line!?
2025-01-27 14:09:32 -08:00
Howard Abrams
cc84bff616 Migrate Mastodon to new server
Also, switching my Matrix account too.
2025-01-27 10:54:13 -08:00
Howard Abrams
14e023c730 Writing my own MUD Client for my MUD Server 2025-01-22 09:40:06 -08:00
Howard Abrams
2cd9a78e29 Fixed eshell's banner ... again
This time for realz!
2025-01-20 08:33:38 -08:00
12 changed files with 511 additions and 82 deletions

View file

@ -1,8 +1,8 @@
#+title: My Emacs Configuration #+TITLE: My Emacs Configuration
#+author: Howard X. Abrams #+AUTHOR: Howard X. Abrams
#+date: 2021-11-01 #+DATE: 2021-11-01
#+filetags: emacs readme #+FILETAGS: emacs readme
#+startup: inlineimages #+STARTUP: inlineimages
** Introduction ** Introduction
Ive crafted my Emacs configuration, I cheekily call /hamacs/, in a literate programming model, heavily inspired by my recent journey into [[https://www.youtube.com/watch?v=LKegZI9vWUU][Henrik Lissner's]] [[https://github.com/hlissner/doom-emacs][Doom Emacs]] and [[https://www.spacemacs.org/][Spacemacs]]. While I used both extensively, I decided I would /roll my own/ as Emacs people like myself, tend to be /control freaks/ (at least a little bit). Ive crafted my Emacs configuration, I cheekily call /hamacs/, in a literate programming model, heavily inspired by my recent journey into [[https://www.youtube.com/watch?v=LKegZI9vWUU][Henrik Lissner's]] [[https://github.com/hlissner/doom-emacs][Doom Emacs]] and [[https://www.spacemacs.org/][Spacemacs]]. While I used both extensively, I decided I would /roll my own/ as Emacs people like myself, tend to be /control freaks/ (at least a little bit).
@ -12,7 +12,7 @@ Using [[https://howardism.org/Technical/Emacs/literate-devops.html][literate pro
Ive separated my configuration into /chapters/ around particular subjects, applications and programming languages. This feature allows you, dear reader, to jump our to items of interest, but allows me to /selectively load/ individual chapters. For instance, if Im not doing much with Ruby at work, I can remove that chapter from the list in my [[file:bootstrap.org::*Load the Rest][bootstrap]]. I also dont load my [[file:ha-display.org][UI configuration]] when I am using the Terminal (doesnt happen much, actually). Ive separated my configuration into /chapters/ around particular subjects, applications and programming languages. This feature allows you, dear reader, to jump our to items of interest, but allows me to /selectively load/ individual chapters. For instance, if Im not doing much with Ruby at work, I can remove that chapter from the list in my [[file:bootstrap.org::*Load the Rest][bootstrap]]. I also dont load my [[file:ha-display.org][UI configuration]] when I am using the Terminal (doesnt happen much, actually).
Hit me up with questions on Mastodon: [[https://emacs.ch/@howard][@howard@emacs.ch]]. Hit me up with questions on Mastodon: [[https://emacs.ch/@howard][@howard@pdx.social]].
If you want to try the entire process, after installing Emacs (see my instructions for [[file:README-MacOS.org][both MacOS]] and [[file:README-Linux.org][Linux]]), clone this repo with: If you want to try the entire process, after installing Emacs (see my instructions for [[file:README-MacOS.org][both MacOS]] and [[file:README-Linux.org][Linux]]), clone this repo with:
#+begin_src sh #+begin_src sh
@ -29,15 +29,15 @@ To create [[file:~/.emacs.d/init.el][~/.emacs.d/init.el]] which starts the proce
- [[file:bootstrap.org][Bootstrap]] :: configures =straight= and loads basic libraries the rest of the code depends on. It then loads the following files in order. - [[file:bootstrap.org][Bootstrap]] :: configures =straight= and loads basic libraries the rest of the code depends on. It then loads the following files in order.
- [[file:ha-config.org][Configuration]] :: contains /most/ of my configuration, setting up my sequence key menus, evil, etc. - [[file:ha-config.org][Configuration]] :: contains /most/ of my configuration, setting up my sequence key menus, evil, etc.
- [[file:ha-evil.org][Evilness]] :: configuration for using VI, er, ~vim~ keybindings in Emacs. - [[file:ha-evil.org][Evilness]] :: configuration for using VI, er, ~vim~ keybindings in Emacs.
- [[file:ha-general.org][Leader]] :: using the ~SPC~ to kick off a hierarchal order of functions. - [[file:ha-general.org][Leader]] :: using the ~SPC~ to kick off a hierarchical order of functions.
- [[file:ha-display.org][GUI Display]] :: sets up the visual aspects of an Emacs GUI, including themes and fonts. - [[file:ha-display.org][GUI Display]] :: sets up the visual aspects of an Emacs GUI, including font specification and [[file:ha-theme.org][my theme]].
- [[file:ha-dashboard.org][Dashboard]] :: sets up initial window layout of the =main= project with a dashboard. - [[file:ha-dashboard.org][Dashboard]] :: sets up initial window layout of the =main= project with a dashboard.
- [[file:ha-data.org][Data]] :: functions for dealing with a buffer-full of data. - [[file:ha-data.org][Data]] :: functions for dealing with a buffer-full of data.
** Org Mode Configuration ** Org Mode Configuration
- [[file:ha-org.org][Initial Org Configuration]] :: configures the basics for org-mode formatted files. Specific features come from their own files. - [[file:ha-org.org][Initial Org Configuration]] :: configures the basics for org-mode formatted files. Specific features come from their own files.
- [[file:ha-org-word-processor.org][Word Processing]] :: attempts to make Org files /visually/ look like a word processor, including turning off the colors for headers, and instead increasing their size. - [[file:ha-org-word-processor.org][Word Processing]] :: attempts to make Org files /visually/ look like a word processor, including turning off the colors for headers, and instead increasing their size.
- [[file:ha-org-literate.org][Literate Programming]] :: functions to support literate programming techniques. I use this most prevalently with this Emacs configuration. - [[file:ha-org-literate.org][Literate Programming]] :: functions to support literate programming techniques. I use this with my Emacs configuration.
- [[file:ha-org-clipboard.org][Clipboard]] :: automatically converting HTML from a clipboard into Org-formatted content. - [[file:ha-org-clipboard.org][Clipboard]] :: automatically converting HTML from a clipboard into Org-formatted content.
- [[file:ha-org-journaling.org][Journaling]] :: for writing journal entries and tasks. - [[file:ha-org-journaling.org][Journaling]] :: for writing journal entries and tasks.
- [[file:ha-org-publishing.org][Publishing]] :: code for publishing my website, [[http://howardism.org][www.howardism.org]]. - [[file:ha-org-publishing.org][Publishing]] :: code for publishing my website, [[http://howardism.org][www.howardism.org]].
@ -82,3 +82,7 @@ Other functions and files come from essays written on [[http://www.howardism.org
#+options: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil #+options: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
#+options: skip:nil author:nil email:nil creator:nil timestamp:nil #+options: skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt: view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js #+infojs_opt: view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
# Local Variables:
# jinx-local-words: "bitlbee rcirc supe"
# End:

View file

@ -32,6 +32,19 @@ I'm installing everything using the [[https://github.com/raxod502/straight.el#ge
See the details in [[https://dev.to/jkreeftmeijer/emacs-package-management-with-straight-el-and-use-package-3oc8][this essay]]. See the details in [[https://dev.to/jkreeftmeijer/emacs-package-management-with-straight-el-and-use-package-3oc8][this essay]].
* Initial Settings * Initial Settings
** Garbage Collection Settings
GC has always been a contentious subject in Emacs. Lot less of an issue now, but lets not slow the startup (any more than I already do by checking all the packages to see if anything new needs to be downloaded).
Limit garbage collection before startup and then go back to the default value (8 MiB) after startup.
#+BEGIN_SRC emacs-lisp
(setq gc-cons-threshold most-positive-fixnum)
(add-hook 'emacs-startup-hook
(lambda ()
(setq gc-cons-threshold (* 100 1024 1024))))
#+END_SRC
** OS Path and Native Compilation ** OS Path and Native Compilation
Helper functions to allow code for specific operating systems: Helper functions to allow code for specific operating systems:
#+begin_src emacs-lisp #+begin_src emacs-lisp

View file

@ -33,7 +33,7 @@ Glad to see the 2FA feature is working on the [[https://codeberg.org/martianh/ma
(use-package mastodon (use-package mastodon
:straight (:host codeberg :repo "martianh/mastodon.el") :straight (:host codeberg :repo "martianh/mastodon.el")
:init :init
(setq mastodon-instance-url "https://pdx.sh" (setq mastodon-instance-url "https://pdx.social"
mastodon-active-user "howard")) mastodon-active-user "howard"))
#+end_src #+end_src
@ -97,15 +97,26 @@ Yet another encrypted chat/VoIP client-server, but unlike Signal and Telegram, [
(use-package ement (use-package ement
:straight (:host github :repo "alphapapa/ement.el") :straight (:host github :repo "alphapapa/ement.el")
:config :config
(major-mode-hydra-define ement-room-mode (:quit-key "q")
("Send"
(("c" ement-room-compose-message "Compose Message")
("d" ement-send-direct-message "Direct Message")
("S" ement-room-dispatch-reply-to-message "Message Reply")
("s" ement-room-send-message "Send (or <Return>)"))
"Other"
(("r" ement-room-send-reaction "React")
("e" ement-room-send-emote "Emote")
("R" ement-view-room "Jump to Room"))))
(defun ha-ement-connect () (defun ha-ement-connect ()
(interactive) (interactive)
(let* ((auth-results (auth-source-search :host "howardism-matrix")) (let* ((auth-results (auth-source-search :host "matrix"))
(auth-first (first auth-results)) (auth-first (first auth-results))
(username (plist-get auth-first :user)) (username (plist-get auth-first :user))
(password (funcall (plist-get auth-first :secret)))) (password (funcall (plist-get auth-first :secret))))
(ement-connect :user-id username (ement-connect :user-id username
:password password :password password
:uri-prefix "https://howardism.org")) :uri-prefix "https://matrix.org"))
(ha-leader (ha-leader
"a x S" '("send" . ement-send-direct-message) "a x S" '("send" . ement-send-direct-message)

View file

@ -1,7 +1,8 @@
#+title: General Emacs Configuration #+TITLE: General Emacs Configuration
#+author: Howard X. Abrams #+AUTHOR: Howard X. Abrams
#+date: 2020-09-10 #+DATE: 2020-09-10
#+tags: emacs #+FILETAGS: emacs
#+STARTUP: inlineimages
A literate programming file for configuring Emacs. A literate programming file for configuring Emacs.
@ -24,12 +25,15 @@ A literate programming file for configuring Emacs.
;; ;;
;;; Commentary: ;;; Commentary:
;; ;;
;; Basic configuration of Emacs. Start early in the loading sequence. ;; Basic Emacs configuration settings, ran near the beginning of the
;; loading sequence.
;; ;;
;;; Code: ;;; Code:
#+end_src #+end_src
* Basic Configuration * Basic Configuration
I begin configuration of Emacs that isnt /package-specific/. For instance, I hate to fat-finger a single letter that could stop Emacs: I begin configuration of Emacs that isnt /package-specific/.
I hate to fat-finger a single letter that could stop Emacs:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(setq confirm-kill-emacs 'yes-or-no-p) (setq confirm-kill-emacs 'yes-or-no-p)
#+end_src #+end_src

View file

@ -22,6 +22,12 @@ A literate programming file to configure the Emacs UI.
;; ~/src/hamacs/ha-display.org ;; ~/src/hamacs/ha-display.org
;; Using `find-file-at-point', and tangle the file to recreate this one . ;; Using `find-file-at-point', and tangle the file to recreate this one .
;; ;;
;;; Commentary:
;;
;; Configuration settings related to graphical display. Ran when the setting
;; `display-graphic-p' is non-nil. Change the fonts, colors and ligatures;
;; settings unavailable when ran from a Terminal emulator.
;;
;;; Code: ;;; Code:
#+end_src #+end_src
@ -288,19 +294,27 @@ brew tap homebrew/cask-fonts
brew install --cask font-hack-nerd-font brew install --cask font-hack-nerd-font
#+end_src #+end_src
** Specifying a Font ** Specifying a Font
My /current/ favorite font is actually the top list of fonts that may be installed on my system:
My /current/ favorite /coding/ font changes often…call me /font-curious/. Since I may/may not have each font installed, I make a list, and pick the first one installed, so I order them:
- While I like Microsofts [[https://github.com/microsoft/cascadia-code][Cascadia]], Im using [[https://github.com/eliheuer/caskaydia-cove][Caskaydia Cove]] from our beloved [[https://www.nerdfonts.com/font-downloads][NerdFonts]] as it has:
- A dot in the 0
- Good distinguishing aspects between parens, brackets and braces
- Medium level of ligatures, like -> for arrows, but triple === signs dont make three lines
- Less serifs mean less letters
- [[https://github.com/emersion/nanum-gothic-coding][Nanum Gothic Coding]] won the [[https://www.codingfont.com][CodingFont Challenge]] for me, like Hack (a fav) but with ligatures
- [[https://github.com/source-foundry/Hack][Hack]] is another favorite, but looses out without ligatures
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defvar ha-fixed-font (defvar ha-fixed-font
(when window-system (when window-system
(or (or
(seq-first (seq-first
(seq-filter (lambda (font) (when (x-list-fonts font) font)) (seq-filter (lambda (font) (when (x-list-fonts font) font))
'("CaskaydiaCove Nerd Font" ; finally found it '("CaskaydiaCove Nerd Font" ; Best Nerd-based font
;; funky font with litagures and a dotted 0 "NanumGothicCoding" ; Winner of codingfont.com
"Cascadia Code PL" "Hack Nerd Font" ; no litagures!?
;; clean font, but no litagures!? "FiraCode Nerd Font" ; has too much ligatures
"Hack Nerd Font"
"FiraCode Nerd Font" ; has litagures
"Cousine Nerd Font" "Cousine Nerd Font"
"Iosevka Nerd Font" "Iosevka Nerd Font"
"FantasqueSansMono Nerd Font" "FantasqueSansMono Nerd Font"
@ -311,21 +325,19 @@ My /current/ favorite font is actually the top list of fonts that may be install
"My fixed width font based on what I have installed.") "My fixed width font based on what I have installed.")
#+end_src #+end_src
I probably don't need to have such a ranking system, as chances are good I have them all installed. While I like [[https://www.brailleinstitute.org/freefont/][Atkinson Hyperlegible]] a lot (oh, and [[https://fontesk.com/xcharter-typeface/][Literata]]), I found that [[https://supernotes.app/open-source/sn-pro][SN Pro]] is great for headers as well as matches my monospace font, [[https://github.com/eliheuer/caskaydia-cove/][Caskaydia Cove]].
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defvar ha-variable-font (defvar ha-variable-font
(when window-system (when window-system
(or (or
(seq-first (seq-first
(seq-filter (lambda (font) (when (x-list-fonts font) font)) (seq-filter (lambda (font) (when (x-list-fonts font) font))
'("Atkinson Hyperlegible" '("SN Pro"
"SN Pro" ; https://supernotes.app/open-source/sn-pro "Atkinson Hyperlegible"
"Literata" ; Clean, readable with litagures "Literata"
;; Next best can be downloaded here:
;; https://fontesk.com/xcharter-typeface/
"XCharter" "XCharter"
"Charter" "Charter"
;; Interesting idea: "Iosevka Comfy Motion Duo"
"Serif"))) "Serif")))
(warn "Cannot find a Serif Font. Install Source Sans Pro.")))) (warn "Cannot find a Serif Font. Install Source Sans Pro."))))

View file

@ -31,7 +31,7 @@ Tell straight to use the built-in =eshell=:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package eshell (use-package eshell
:straight (:type built-in) :straight (:type built-in)
:hook (eshell-mode . 'ha-eshell-setup)) :hook (eshell-mode . ha-eshell-setup))
#+end_src #+end_src
After reading [[https://xenodium.com/my-emacs-eye-candy/][this essay]], I decided to try hiding the mode line in eshell windows … at least, until I get the mode line to display more important information. Note that hiding the mode line is fairly straight-forward, but others might want to use the [[https://github.com/hlissner/emacs-hide-mode-line][hide-mode-line]] package that turns that /mode-line definition/ into a minor mode that can be toggled. After reading [[https://xenodium.com/my-emacs-eye-candy/][this essay]], I decided to try hiding the mode line in eshell windows … at least, until I get the mode line to display more important information. Note that hiding the mode line is fairly straight-forward, but others might want to use the [[https://github.com/hlissner/emacs-hide-mode-line][hide-mode-line]] package that turns that /mode-line definition/ into a minor mode that can be toggled.
@ -39,10 +39,54 @@ After reading [[https://xenodium.com/my-emacs-eye-candy/][this essay]], I decide
I like =debug-on-error=, but not in Eshell, so I set this up when entering Eshell: I like =debug-on-error=, but not in Eshell, so I set this up when entering Eshell:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun ha-eshell-setup () (defun ha-eshell-setup ()
(make-local-variable 'debug-on-error) (set (make-local-variable 'debug-on-error) nil)
(setq mode-line-format nil (setq mode-line-format nil))
debug-on-error nil))
#+end_src #+end_src
** Directory Notification
While most people put the “current directory” in their shell prompt, Ive always found that pretty distracting, difficult when copy/pasting commands, and cuts out on the length of my commands (which might be a good thing, tbh).
I use the =header-line= for this.
#+BEGIN_SRC emacs-lisp
(defun ha-eshell-header-line ()
"Update the buffer-local `header-line-format' with the current directory.
This also calls `ha-eshell-git-branch' to format the Git repo string."
(let* ((branch (ha-eshell-git-branch))
(git-icon (all-the-icons-octicon "git-branch"))
(git-line (if branch (format "%s %s" git-icon branch) ""))
(home-rx (rx (literal (getenv "HOME"))))
(dir-line (thread-last default-directory
(replace-regexp-in-string home-rx "~")
(directory-file-name))))
(setq header-line-format
(format " %s %s" dir-line git-line))))
#+END_SRC
Which depends on a function to get the current branch and git status. How else but shelling out for this information?
#+BEGIN_SRC emacs-lisp
(defun ha-eshell-git-branch ()
"Return simplified Git branch for current directory."
(ignore-errors
(thread-last "git status --short --branch --ahead-behind 2>/dev/null"
(shell-command-to-list)
(first)
(replace-regexp-in-string
(rx "## "
(group (zero-or-more not-newline))
(zero-or-more anychar))
"\\1")
(replace-regexp-in-string
(rx "...") " → "))))
#+END_SRC
Need to hook this when we change the directory.
#+BEGIN_SRC emacs-lisp
(use-package eshell
:hook (eshell-directory-change . ha-eshell-header-line))
#+END_SRC
** Navigation and Keys ** Navigation and Keys
Along with the regular Emacs keybindings, Eshell comes with some interesting features: Along with the regular Emacs keybindings, Eshell comes with some interesting features:
- ~M-RET~ gives you a prompt, even when you are running another command. Since =eshell= passes all input to subprocesses, there is no automatic input queueing as there is with other shells. - ~M-RET~ gives you a prompt, even when you are running another command. Since =eshell= passes all input to subprocesses, there is no automatic input queueing as there is with other shells.
@ -1326,7 +1370,7 @@ The [[https://codeberg.org/akib/emacs-eat][Emulate a Terminal]] project provides
(use-package eat (use-package eat
:after eshell :after eshell
:straight (:repo "https://codeberg.org/akib/emacs-eat") :straight (:repo "https://codeberg.org/akib/emacs-eat")
:hook (eshell-load . #'eat-eshell-visual-command-mode)) :hook (eshell-load . eat-eshell-visual-command-mode))
#+end_src #+end_src
Note that the =eat-eshell-visual-command-mode= also kicks off the global minor mode, =eat-eshell-mode=. The big advantage of Eat is the /three input modes/, however, in Eshell with Evil, I can just type ~Escape~ to go into Emacs Mode, and ~G A~ to return to typing Terminal commands. Note that the =eat-eshell-visual-command-mode= also kicks off the global minor mode, =eat-eshell-mode=. The big advantage of Eat is the /three input modes/, however, in Eshell with Evil, I can just type ~Escape~ to go into Emacs Mode, and ~G A~ to return to typing Terminal commands.
@ -1495,41 +1539,18 @@ The [[http://projects.ryuslash.org/eshell-fringe-status/][eshell-fringe-status]]
:hook (eshell-mode . eshell-fringe-status-mode)) :hook (eshell-mode . eshell-fringe-status-mode))
#+end_src #+end_src
** Opening Banner ** Opening Banner
Whenever I open a shell, I instinctively type =ls= … so why not do that automatically? The [[elisp:(describe-variable 'eshell-banner-message)][eshell-banner-message]] variable, while defaults to a string, this variable can be a /form/ (an s-expression) that calls a function, so I made a customized =ls= that can be attractive:
Whenever I open a shell, I instinctively type =ls= … so why not do that automatically? The [[elisp:(describe-variable 'eshell-banner-message)][eshell-banner-message]] variable, while defaults to a string, this variable can be a /form/ (an s-expression) that calls a function, so I call my customized =lsd= to be more attractive:
#+begin_src emacs-lisp #+begin_src emacs-lisp
(defun ha-eshell-banner () (defun ha-eshell-banner ()
"Return a string containing the files in the current directory." "Return a string containing the files in the current directory."
(let ((fg (face-attribute 'default :background)) (ha-eshell-header-line)
(bg (face-attribute 'default :foreground)) (condition-case err
(bg "#c09644") (eshell/lsd)
(dd (replace-regexp-in-string (getenv "HOME") "~" default-directory)) (error "🐚 Welcome to Eshell\n\n")))
(gs (or (ha-eshell-banner-git-branch) "")))
(condition-case err
(concat
;; Line 1
(propertize
(format " %s • ⑆ %s " dd gs)
'face `(:background ,bg :foreground ,fg))
"\n"
;; Line 2
(ha-dad-joke)
"\n\n")
(error "🐚 Welcome to Eshell\n\n"))))
(defun ha-eshell-banner-git-branch ()
"Return simplified Git branch for current directory."
(ignore-errors
(thread-last "git status --short --branch --ahead-behind 2>/dev/null"
(shell-command-to-list)
(first)
(replace-regexp-in-string
(rx "## "
(group (zero-or-more not-newline))
(zero-or-more anychar))
"\\1")
(replace-regexp-in-string
(rx "...") " → "))))
#+end_src #+end_src
* Shell Windows * Shell Windows
Now that I often need to pop into remote systems to run a shell or commands, I create helper functions to create those buffer windows. Each buffer begins with =eshell=: allowing me to have more than one eshells, typically, one per project. Now that I often need to pop into remote systems to run a shell or commands, I create helper functions to create those buffer windows. Each buffer begins with =eshell=: allowing me to have more than one eshells, typically, one per project.
** Shell There ** Shell There
@ -1745,7 +1766,6 @@ Here is where we associate all the functions and their hooks with =eshell=, thro
(use-package eshell (use-package eshell
:straight (:type built-in) :straight (:type built-in)
:custom (eshell-banner-message '(ha-eshell-banner)) :custom (eshell-banner-message '(ha-eshell-banner))
:init :init
(setq eshell-error-if-no-glob t (setq eshell-error-if-no-glob t
;; This jumps back to the prompt: ;; This jumps back to the prompt:
@ -1762,7 +1782,7 @@ Here is where we associate all the functions and their hooks with =eshell=, thro
;; Me neither, so this makes it act a bit more shell-like: ;; Me neither, so this makes it act a bit more shell-like:
eshell-prefer-lisp-functions nil) eshell-prefer-lisp-functions nil)
:hook ((eshell-pred-load . ha-eshell-add-predicates)) ;; :hook ((eshell-pred-load . ha-eshell-add-predicates))
:bind (("M-!" . eshell-command) :bind (("M-!" . eshell-command)
:map eshell-mode-map :map eshell-mode-map
@ -1770,6 +1790,7 @@ Here is where we associate all the functions and their hooks with =eshell=, thro
("C-d" . ha-eshell-quit-or-delete-char))) ("C-d" . ha-eshell-quit-or-delete-char)))
#+end_src #+end_src
Note that the default list to [[elisp:(describe-variable 'eshell-visual-commands)][eshell-visual-commands]] is good enough, but some of my /newer/ Rust-based apps need to be added: Note that the default list to [[elisp:(describe-variable 'eshell-visual-commands)][eshell-visual-commands]] is good enough, but some of my /newer/ Rust-based apps need to be added:
#+begin_src emacs-lisp :tangle no #+begin_src emacs-lisp :tangle no
(use-package eshell (use-package eshell
:config :config

View file

@ -2,7 +2,7 @@
#+author: Howard Abrams #+author: Howard Abrams
#+date: 2024-07-07 #+date: 2024-07-07
#+filetags: emacs hamacs #+filetags: emacs hamacs
#+lastmod: [2024-11-11 Mon] #+lastmod: [2025-01-28 Tue]
A literate programming file for literate programming in Emacs Org Files. A literate programming file for literate programming in Emacs Org Files.
@ -578,7 +578,7 @@ At this point, we can jump to functions and variables that I define in my org fi
I can jump around my literate code as if they were =.el= files. I may want to think about expanding the definitions to figure out the language of the destination. I can jump around my literate code as if they were =.el= files. I may want to think about expanding the definitions to figure out the language of the destination.
** Noweb References ** Noweb References
A noweb definition, e.g. =<<something-something>>= should /jump/ to the =#name= definition. A noweb definition, e.g. =<<something-something>>= should /jump/ to the =#name= definition.
Since [[https://github.com/BurntSushi/ripgrep][ripgrep]] is pretty fast, Ill call it instead of attempting to build a [[https://stackoverflow.com/questions/41933837/understanding-the-ctags-file-format][CTAGS]] table. Oooh, the =rg= takes a =—json= option, which makes it easier to parse. Since [[https://github.com/BurntSushi/ripgrep][ripgrep]] is pretty fast, Ill call it instead of attempting to build a [[https://stackoverflow.com/questions/41933837/understanding-the-ctags-file-format][CTAGS]] table. Oooh, the =rg= takes a =—json= option, which makes it easier to parse.

View file

@ -101,7 +101,7 @@ Need the IDE features associated with [[https://github.com/clojure-emacs/cider][
;; backward-word, etc) in the REPL is quite useful since we often have ;; backward-word, etc) in the REPL is quite useful since we often have
;; to deal with Java class and method names. The built-in Emacs minor ;; to deal with Java class and method names. The built-in Emacs minor
;; mode subword-mode provides such functionality ;; mode subword-mode provides such functionality
:hook (cider-repl-mode . #'subword-mode) :hook (cider-repl-mode . subword-mode)
:config :config
(ha-local-leader :keymaps clojure-mode-map (ha-local-leader :keymaps clojure-mode-map

View file

@ -55,7 +55,7 @@ The [[https://github.com/mihaimaruseac/hindent][hindent package]] looks interest
#+begin_src emacs-lisp #+begin_src emacs-lisp
(use-package hindent (use-package hindent
:custom (hindent-style "johan-tibell") :custom (hindent-style "johan-tibell")
:hook (haskell-mode . #'hindent-mode)) :hook (haskell-mode . hindent-mode))
#+end_src #+end_src
* Haskell and Org * Haskell and Org
#+begin_src emacs-lisp #+begin_src emacs-lisp

View file

@ -252,6 +252,7 @@ Color definition injects the /named/ lists defined above (using Orgs =noweb=
) )
"A list of named colors available in theme.") "A list of named colors available in theme.")
#+END_SRC #+END_SRC
* Theme Support * Theme Support
Stole the following macro from Zenburn, which converts color references defined above, but only within the body of the macro. Sweet way to trim down a lot of boilerplate: Stole the following macro from Zenburn, which converts color references defined above, but only within the body of the macro. Sweet way to trim down a lot of boilerplate:
@ -341,16 +342,16 @@ Lets make a /theme/:
`(line-number ((t (:foreground ,gray-50 :background ,gray-10)))) `(line-number ((t (:foreground ,gray-50 :background ,gray-10))))
`(line-number-current-line ((t (:foreground ,gray-95 :background ,gray-20 :weight ultra-bold)))) `(line-number-current-line ((t (:foreground ,gray-95 :background ,gray-20 :weight ultra-bold))))
`(header-line ((t (:foreground ,gray-80)))) `(header-line ((t (:foreground ,almond :background ,inactive :extend t))))
`(help-key-binding ((t (:foreground ,gray-80 :weight ultra-bold)))) `(help-key-binding ((t (:foreground ,gray-80 :weight ultra-bold))))
`(bold ((t (:foreground ,gray-90 :weight ultra-bold)))) `(bold ((t (:foreground ,gray-90 :weight ultra-bold))))
`(italics ((t (:foreground ,gray-95)))) `(italics ((t (:foreground ,gray-95 :slant italic))))
`(bold-italic ((t (:foreground "white")))) `(bold-italic ((t (:foreground "white" :slant italic :weight ultra-bold))))
`(link ((t (:foreground ,link-color)))) `(link ((t (:foreground ,link-color))))
`(link-visited ((t (:foreground ,visited-color)))) `(link-visited ((t (:foreground ,visited-color))))
`(font-lock-comment-face ((t (:foreground ,gray-60)))) `(font-lock-comment-face ((t (:foreground ,gray-60 :slant italic))))
`(font-lock-comment-delimiter-face ((t (:foreground ,gray-50)))) `(font-lock-comment-delimiter-face ((t (:foreground ,gray-50))))
`(font-lock-string-face ((t (:foreground ,gray-75)))) `(font-lock-string-face ((t (:foreground ,gray-75))))
`(font-lock-type-face ((t (:foreground ,green-lt)))) `(font-lock-type-face ((t (:foreground ,green-lt))))

View file

@ -31,6 +31,9 @@ cat > "$HAMACS_DEST/early-init.el" <<EOF
;; ;;
;;; Code: ;;; Code:
;; Need the package system near the front:
(require 'package)
;; We'll be using straight. So, we don't want duplicated package loading: ;; We'll be using straight. So, we don't want duplicated package loading:
(setq package-enable-at-startup nil) (setq package-enable-at-startup nil)
@ -68,9 +71,9 @@ cat > "$HAMACS_DEST/init.el" <<EOF
;; ;;
;;; Commentary: ;;; Commentary:
;; ;;
;; This is my Emacs Bootloader. Simply put, I initialize the package management ;; This is my Emacs Bootloader. Simply put, I initialize the package
;; system, and then tangle my literate files. This simple idea came from ;; management system, and then tangle my literate files. This simple
;; https://github.com/susamn/dotfiles ;; idea came from https://github.com/susamn/dotfiles
;; ;;
;;; Code: ;;; Code:
@ -79,9 +82,22 @@ cat > "$HAMACS_DEST/init.el" <<EOF
;; Bug fixes for ORG (there always seems to be something): ;; Bug fixes for ORG (there always seems to be something):
(defvar native-comp-deferred-compilation-deny-list nil) (defvar native-comp-deferred-compilation-deny-list nil)
;; Allow the installation of unsigned packages, but verify the signature if possible: ;; Allow the installation of unsigned packages, but verify the
;; signature if possible:
(setq package-check-signature 'allow-unsigned) (setq package-check-signature 'allow-unsigned)
;; While using Straight with direct Github repos,
;; adding Melpa and others isn't a bad idea:
(require 'use-package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/"))
(add-to-list 'package-archives
'("elpa-dev" . "https://elpa.gnu.org/devel/"))
;; Configure straight https://github.com/raxod502/straight.el#getting-started ;; Configure straight https://github.com/raxod502/straight.el#getting-started
(defvar bootstrap-version) (defvar bootstrap-version)

347
pud.org Normal file
View file

@ -0,0 +1,347 @@
#+title: Moss and Puddles
#+author: Howard X. Abrams
#+date: 2025-01-18
#+filetags: emacs hamacs
#+lastmod: [2025-01-20 Mon]
A literate programming file for a Comint-based MUD client.
#+begin_src emacs-lisp :exports none
;;; pud --- a MUD client -*- lexical-binding: t; -*-
;;
;; © 2025 Howard X. Abrams
;; Licensed under a Creative Commons Attribution 4.0 International License.
;; See http://creativecommons.org/licenses/by/4.0/
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
;; Maintainer: Howard X. Abrams
;; Created: January 18, 2025
;;
;; While obvious, GNU Emacs does not include this file or project.
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; /Users/howard/src/hamacs/pud.org
;; And tangle the file to recreate this one.
;;
;;; Code:
#+end_src
* Introduction
This project is a simple MUD client for Emacs, based on COM-INT MUD client based on Mickey Petersens [[https://www.masteringemacs.org/article/comint-writing-command-interpreter][essay on Comint]].
The default connects to *Moss n Puddles*, my own MUD which I invite you to join.
#+BEGIN_SRC emacs-lisp
(defvar pud-default-world "moss-n-puddles"
"The default world to connect.")
#+END_SRC
This uses =telnet= (at the moment) for the connection, so you will need to install that first. On Mac, this would be:
#+BEGIN_SRC sh
brew install telent
#+END_SRC
And use a similar command on Linux.
* User Credentials
You may want to customize your connections to more worlds.
#+BEGIN_SRC emacs-lisp
(defgroup pud nil
"An overly simplistic MUD client that works with Evennia."
:group 'processes)
(defcustom pud-worlds
(list (vector pud-default-world "localhost" "4000" "guest" "guest"))
"List of worlds you play in.
You need to define the worlds you play in before you can get
started. In most worlds, you can start playing using a guest account.
Each element WORLD of the list has the following form:
\[NAME HOST PORT CHARACTER PASSWORD]
NAME identifies the connection, HOST and PORT specify the network
connection, CHARACTER and PASSWORD are used to connect automatically.
Note that this will be saved in your `custom-file' -- including your
passwords! If you don't want that, specify nil as your password."
:type '(repeat
(vector :tag "World"
(string :tag "Name")
(string :tag "Host")
(integer :tag "Port")
(string :tag "Char" :value "guest")
(string :tag "Pwd" :value "guest")))
:group 'pud)
#+END_SRC
Next, open [[file:~/.authinfo.gpg][your authinfo file]], and insert the following line, substituting =[user]= and =[pass]= with your credentials as well as the first entry to match your world:
#+BEGIN_SRC conf :tangle no :eval no
machine moss-n-puddles login [name] password [pass]
#+END_SRC
Now, lets play! Type =run-pud=, and optionally select a world. If you get disconnected, re-run it, or even =pud-reconnect=.
The rest of this file describes the code to implement this project.
* Code
The following function will return the default world:
#+BEGIN_SRC emacs-lisp
(defun pud-get-default-world ()
"Return the connection information for the `pud-default-world'.
If only one world listed in `pud-worlds', return that."
(if (length= pud-worlds 1)
(seq-first pud-worlds)
(seq-find
(lambda (w) (string-equal (aref w 0) pud-default-world))
pud-worlds)))
#+END_SRC
And accessibility functions.
#+BEGIN_SRC emacs-lisp
(defsubst pud-world-name (&optional world)
"Return the name for WORLD as a string."
;; (concat (aref world 3) "@" (aref world 0))
(if (vectorp world)
(aref world 0)
world))
(defsubst pud-world-network (&optional world)
"Return the network details for WORLD as a cons cell (HOST . PORT)."
(unless world
(setq world (pud-get-default-world)))
(list (aref world 1) (aref world 2)))
(defsubst pud-world-character (&optional world)
"Return the character for WORLD as a string.
Override the customized setting if the world has an entry in authinfo."
(unless world
(setq world (pud-get-default-world)))
(if-let ((auth-results (auth-source-search :host (aref world 0))))
(thread-first auth-results
(first)
(plist-get :user))
(aref world 3)))
(defsubst pud-world-password (&optional world)
"Return the password for WORLD as a string."
(unless world
(setq world (pud-get-default-world)))
(if-let ((auth-results (auth-source-search :host (aref world 0))))
(thread-first auth-results
(first)
(plist-get :secret)
(funcall))
(aref world 4)))
#+END_SRC
And some basic functions that really need to be expanded.
#+BEGIN_SRC emacs-lisp :tangle no
(ert-deftest pud-world-name-test ()
(should (string-equal (pud-world-name "foobar") "foobar"))
(should (string-equal (pud-world-name ["foobar" "localhost" "4000" "guest" "guest"]) "foobar")))
(ert-deftest pud-world-network-test ()
(should (equal (pud-world-network) '("localhost" "4000")))
(should (equal (pud-world-network ["foobar" "overthere" "4000" "guest" "guest"]) '("overthere" "4000"))))
(ert-deftest pud-world-character-test ()
(should (equal (pud-world-character) "guest")))
#+END_SRC
Choosing a world… er, connection using a =completing-read= allowing you to choose a world. If =pud-worlds= contains a single value, might as well just return that.
#+BEGIN_SRC emacs-lisp
(defvar pud-world-history nil
"History for `pud-get-world'.")
(defun pud-get-world ()
"Let the user choose a world from `pud-worlds'.
The return value is a cons cell, the car is the name of the connection,
the cdr holds the connection defails from `pud-worlds'."
(if (length= pud-worlds 1)
(seq-first pud-worlds))
(let ((world-completions
(mapcar (lambda (w)
(cons (pud-world-name w) w))
pud-worlds)))
(cond
((and world-completions (length= world-completions 1))
(thread-first world-completions
(first)
(cdr)))
(world-completions
(thread-first
(completing-read "World: " world-completions nil t nil pud-world-history)
(assoc world-completions)
(cdr)))
(t (customize-option 'pud-worlds)))))
#+END_SRC
And a function for the full credentials, which just happens to be what we need to pass to =telnet=.
#+BEGIN_SRC emacs-lisp
(defun pud-credentials (&optional world)
"Reset the credentials from WORLD from the authinfo system."
(setf (elt 3 world) (pud-world-character world))
(setf (elt 4 world) (pud-world-password world))
world)
#+END_SRC
* Basics
Using Comint, and hoping to have the ANSI colors displayed.
#+BEGIN_SRC emacs-lisp
(require 'comint)
(load "ansi-color" t)
#+END_SRC
Im going to use good ol fashion =telnet= for the connection:
#+BEGIN_SRC emacs-lisp
(defvar pud-cli-file-path "/usr/local/bin/telnet"
"Path to the program used by `run-pud'")
#+END_SRC
The pud-cli-arguments, holds a list of commandline arguments: the port.
The empty and currently disused mode map for storing our custom keybindings inherits from =comint-mode-map=, so we get the same keys exposed in =comint-mode=.
#+BEGIN_SRC emacs-lisp
(defvar pud-mode-map
(let ((map (nconc (make-sparse-keymap) comint-mode-map)))
;; example definition
(define-key map "\t" 'completion-at-point)
map)
"Basic mode map for `run-pud'.")
#+END_SRC
This holds a regular expression that matches the prompt style for the MUD. Not sure if this is going to work, since MUDs typically dont have prompts.
#+BEGIN_SRC emacs-lisp
(defvar pud-prompt-regexp "" ; "^\\(?:\\[[^@]+@[^@]+\\]\\)"
"Prompt for `run-pud'.")
#+END_SRC
The name of the buffer:
#+BEGIN_SRC emacs-lisp
(defvar pud-buffer-name "*Moss and Puddles*"
"Name of the buffer to use for the `run-pud' comint instance.")
#+END_SRC
#+BEGIN_SRC emacs-lisp
(defun pud-buffer-name (&optional world)
"Return the buffer name associated with WORLD."
(format "*%s*" (if world
(pud-world-name world)
pud-default-world)))
#+END_SRC
The main entry point to the program is the =run-pud= function:
#+BEGIN_SRC emacs-lisp
(defun run-pud (world)
"Run an inferior instance of `pud-cli' inside Emacs."
(interactive (list (pud-get-world)))
(let* ((pud-program pud-cli-file-path)
(pud-args (pud-world-network world))
(buffer (get-buffer-create (pud-buffer-name world)))
(proc-alive (comint-check-proc buffer))
(process (get-buffer-process buffer)))
;; if the process is dead then re-create the process and reset the
;; mode.
(unless proc-alive
(with-current-buffer buffer
(apply 'make-comint-in-buffer "Pud" buffer pud-program nil pud-args)
(pud-mode)
(visual-line-mode 1)
(pud-reconnect world)))
;; Regardless, provided we have a valid buffer, we pop to it.
(when buffer
(pop-to-buffer buffer))))
#+END_SRC
Connection and/or re-connection:
#+BEGIN_SRC emacs-lisp
(defun pud-reconnect (world)
"docstring"
(interactive (list (pud-get-world)))
(pop-to-buffer (pud-buffer-name world))
(sit-for 1)
(let* ((username (pud-world-character world))
(password (pud-world-password world))
(conn-str (format "connect %s %s\n" username password))
(process (get-buffer-process (current-buffer))))
(if process
(comint-send-string process conn-str)
(insert conn-str))))
#+END_SRC
* Pud Mode
The previous snippet of code dealt with creating and maintaining the buffer and process, and this piece of code enriches it with font locking and mandatory setup. Namely comint-process-echoes which, depending on the mode and the circumstances, may result in prompts appearing twice. Setting it to t is usually a requirement, but do experiment.
#+BEGIN_SRC emacs-lisp
(defun pud--initialize ()
"Helper function to initialize Pud."
(setq comint-process-echoes t)
(setq comint-use-prompt-regexp nil))
(define-derived-mode pud-mode comint-mode "Pud"
"Major mode for `run-pud'.
\\<pud-mode-map>"
;; this sets up the prompt so it matches things like: [foo@bar]
;; (setq comint-prompt-regexp pud-prompt-regexp)
;; this makes it read only; a contentious subject as some prefer the
;; buffer to be overwritable.
(setq comint-prompt-read-only t)
;; this makes it so commands like M-{ and M-} work.
;; (set (make-local-variable 'paragraph-separate) "\\'")
;; (set (make-local-variable 'font-lock-defaults) '(pud-font-lock-keywords t))
;; (set (make-local-variable 'paragraph-start) pud-prompt-regexp)
)
(add-hook 'pud-mode-hook 'pud--initialize)
(defconst pud-keywords
'("connect" "get" "look" "use")
"List of keywords to highlight in `pud-font-lock-keywords'.")
(defvar pud-font-lock-keywords
(list
;; highlight all the reserved commands.
`(,(concat "\\_<" (regexp-opt pud-keywords) "\\_>") . font-lock-keyword-face))
"Additional expressions to highlight in `pud-mode'.")
#+END_SRC
* Technical Artifacts :noexport:
Let's =provide= a name so we can =require= this file:
#+begin_src emacs-lisp :exports none
(provide 'pud)
;;; pud.el ends here
#+end_src
#+DESCRIPTION: a MUD client
#+PROPERTY: header-args:sh :tangle no
#+PROPERTY: header-args:emacs-lisp :tangle yes
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js