New way to display line-numbers. I set mine to =relative= so that I can jump up and down by that value. Set this to =nil= to turn off, or =t= to be absolute.
I like the rendering to curved quotes using [[help:text-quoting-style][text-quoting-style]], because it improves the readability of documentation strings in the =∗Help∗= buffer and whatnot.
As [[https://tecosaur.github.io/emacs-config/config.html][tec wrote]], I want to use =~/.authsource.gpg= as I don’t want to accidentaly purge this file cleaning =~/.emacs.d=, and let's cache as much as possible, as my home machine is pretty safe, and my laptop is shutdown a lot. Also, as [[https://www.bytedude.com/gpg-in-emacs/][bytedude]] mentions, I need to se the =epa-pineentry-mode= to =loopback= to actually get a prompt for the password, instead of an error.
The venerable [[help:hippie-expand][hippie-expand]] function does a better job than the default, [[help:dabbrev-expand][dabbrev-expand]], so let’s swap it out (see this [[https://www.masteringemacs.org/article/text-expansion-hippie-expand][essay]] by Mickey Petersen):
Let’s bind ~TAB~ instead of the default ~M-/~. By default, ~TAB~ re-indents the line, but I find that I want that feature when I’m in Evil’s =normal state= and hit the ~=~ key, so changing this sounds good. But why not /have both/?
Rewriting my shell scripts in Emacs Lisp uses my [[https://gitlab.com/howardabrams/emacs-piper][emacs-piper project]], and this code spills into my configuration code, so let's load it now:
Interested in the [[https://github.com/xenodium/dwim-shell-command][DWIM Shell Command]] project from [[https://xenodium.com/emacs-dwim-shell-command/][Álvaro Ramírez]]. It acts like the regular [[help:shell-command][shell-command]], but the ~q~ closes the results. A command with =<<f>>= operates on the filename of the current buffer, so =chmod a+x <<f>>= works as expected. More useful in [[help:dired][dired]] where you can mark a bunch of files and then operate on all files with such syntax.
Check out [[http://joaotavora.github.io/yasnippet/][the documentation]] for writing them.
Seems the best [[https://github.com/hlissner/doom-snippets][collection of snippets]] is what Henrik Lissner has made for Doom (otherwise, we should use [[http://github.com/AndreaCrotti/yasnippet-snippets][yasnippet-snippets]] package):
The [[https://www.emacswiki.org/emacs/AutoInsertMode][auto-insert]] feature is a wee bit complicated. All I want is to associate a filename regular expression with a YASnippet template. I'm stealing some ideas from Henrik Lissner's [[https://github.com/hlissner/doom-emacs/blob/develop/modules/editor/file-templates/autoload.el][set-file-template!]] macro, but simpler?
Always spaces and never tabs. Note that we use =setq-default= since [[elisp:(describe-variable 'indent-tabs-mode)][indent-tabs-mode]] is a /buffer-local/ variable, meaning using =setq=, sets it for /that buffer file/. We want this globally the default:
When I push changes to my files to Gerrit and other code review, I don’t want trailing spaces or any tabs to appear, so let’s fix all files when I [[elisp:(describe-variable 'before-save-hook)][save them]]:
The [[https://www.emacswiki.org/emacs/RecentFiles][recentf]] feature has been in Emacs for a long time, but it has a problem with Tramp, as we need to turn off the cleanup feature that attempts to =stat= all the files and remove them from the =recent= accessed list if they are readable. The requires recentf to open up a remote files which blocks Emacs at the most inopportune times… like when trying to reboot the machine.
#+begin_src emacs-lisp
(use-package recentf
:straight (:type built-in)
:config
(setq recentf-auto-cleanup 'never) ;; disable before we start recentf!
(recentf-mode 1))
#+end_src
*** File Backups
While I use git as much as I can, sometimes Emacs’ built-in file backup and versioning feature has saved me for files that aren’t.
As [[https://philjackson.github.io//emacs/backups/2022/01/31/keeping-backups-of-every-edited-file/][Phil Jackson]] mentioned, Emacs has a lot of variations to its file backup strategy, and either change the [[help:backup-directory-alist][backup-directory-alist]] to put individual file backups elsewhere, e.g.
After using Ivy, I am going the route of a =completing-read= interface that extends the original Emacs API, as opposed to implementing backend-engines or complete replacements.
The [[https://github.com/minad/vertico][vertico]] package puts the completing read in a vertical format, and like [[https://github.com/raxod502/selectrum#vertico][Selectrum]], it extends Emacs’ built-in functionality, instead of adding a new process. This means all these projects work together.
My issue with Vertico is when calling =find-file=, the Return key opens =dired=, instead of inserting the directory at point. This package addresses this:
This fuzzy completion style is like the built-in =flex= style, but has a better scoring algorithm, non-greedy and ranks completions that match at word; path component; or camelCase boundaries higher.
While flexible at matching, you have to get the /order/ correct. For instance, ~alireg~ matches with [[help:align-regexp][align-regexp]], but ~regali~ does not, so we will use =hotfuzz= for scoring, and not use this as a completion-project (see the =fussy= project below).
While the space can be use to separate words (acting a bit like a =.*= regular expression), the [[https://github.com/oantolin/orderless][orderless]] project allows those words to be in any order.
The [[https://github.com/jojojames/fussy][fussy]] project is a fuzzy pattern matching extension for the normal [[help:completing-read][completing-read]] interface. By default, it uses [[https://github.com/lewang/flx][flx]], but we can specify other sorting and filtering algorithms.
How does it compare? Once upon a time, I enjoyed typing ~plp~ for =package-list-packages=, and when I switched to [[https://github.com/oantolin/orderless][orderless]], I would need to put a space between the words. While I will continue to play with the different mechanism, I’ll combine =hotfuzz= and =orderless=.
Persist history over Emacs restarts using the built-in [[https://www.emacswiki.org/emacs/SaveHist][savehist]] project. Since both Vertico and Selectrum sorts by history position, this should make the choice /smarter/ with time.
The [[https://github.com/minad/marginalia][marginalia]] package gives a preview of =M-x= functions with a one line description, extra information when selecting files, etc. Nice enhancement without learning any new keybindings.
Why use [[https://gitlab.com/ideasman42/emacs-undo-fu][undo-fu]] instead of the built-in undo functionality? Well, there isn’t much to the project (that’s a good thing), but It basically doesn’t /cycle/ around the redo, which annoying.
- [[https://stackoverflow.com/questions/25542097/emacs-evil-mode-how-to-change-insert-state-to-emacs-state-automatically][Evil insert state is really Emacs?]] Real answer to that is to set [[help:evil-disable-insert-state-bindings][evil-disable-insert-state-bindings]]
While I’m pretty good with the VIM keybindings, I would like to play around with the [[https://evil.readthedocs.io/en/latest/extension.html#text-objects][text objects]] and how it compares to others (including the surround), for instance:
- ~’~ :: a string, surround by quotes, also ~`~ for backticks
- ~)~ :: parenthesis, also ~}~ and ~]~, see ~g~
- ~g~ :: within a brace, paren, etc. See below
- ~i~ :: indention area, for YAML and Python
- ~t~ :: an HTML tag
- ~c~ :: for comments
- ~u~ :: for URLs
- ~a~ :: function arguments (probably a lot like symbol, ~o~) with the [[https://github.com/wcsmith/evil-args][evil-args]] extension (that I’m not bothering with)
I took the following clever idea and code from [[http://blog.binchen.org/posts/code-faster-by-extending-emacs-evil-text-object/][this essay]] from Chen Bin for creating a ~xig~ to grab code within any grouping characters, like parens, braces and brackets. For instance, ~dig~ cuts the content inside brackets, etc. First, we need a function to do the work (I changed the original from =my-= to =ha-= so that it is easier for me to distinguish functions from my configuration):
#+begin_src emacs-lisp
(defun ha-evil-paren-range (count beg end type inclusive)
"Get minimum range of paren text object.
COUNT, BEG, END, TYPE is used. If INCLUSIVE is t, the text object is inclusive."
(let* ((parens '("()" "[]" "{}" "<>"))
range
found-range)
(dolist (p parens)
(condition-case nil
(setq range (evil-select-paren (aref p 0) (aref p 1) beg end type count inclusive))
(error nil))
(when range
(cond
(found-range
(when (< (- (nth 1 range) (nth 0 range))
(- (nth 1 found-range) (nth 0 found-range)))
(setf (nth 0 found-range) (nth 0 range))
(setf (nth 1 found-range) (nth 1 range))))
(t
(setq found-range range)))))
found-range))
#+end_src
Extend the text object to call this function for both /inner/ and /outer/:
#+begin_src emacs-lisp
(evil-define-text-object ha-evil-a-paren (count &optional beg end type)
"Select a paren."
:extend-selection t
(ha-evil-paren-range count beg end type t))
(evil-define-text-object ha-evil-inner-paren (count &optional beg end type)
The [[https://github.com/PythonNut/evil-easymotion][evil-easymotion]] project combines [[Jump with Avy][avy]] and evil keybindings, where ~SPC j~ shows labels for all the lines below the cursor, so that you can jump right there. This doesn’t work well with a leader, but what about using Key Chords?
#+begin_src emacs-lisp
(use-package evil-easymotion
:config (evilem-default-keybindings "<f19>"))
#+end_src
My ~F19~ key is within easy reach of my [[https://configure.zsa.io/moonlander/layouts/L4laD/latest/0][Moonlander configuration]], so this might be a good, if somewhat distracting, feature. Perhaps a better solution is to use [[Jump with Avy][avy]] (see below).
The [[https://github.com/edkolev/evil-lion][evil-lion]] package is a wrapper around Emacs’ [[help:align][align]] function. Just a little easier to use. Primary sequence is ~g a i p =~ to align along all the equal characters in the paragraph (block), or ~g a i b RET~ to use a built in rule to align (see below), or ~g a i b /~ to specify a regular expression, similar to [[help:align-regexp][align-regexp]].
#+begin_src emacs-lisp
(use-package evil-lion
:after evil
:bind (:map evil-normal-state-map
("g a" . evil-lion-left)
("g A" . evil-lion-right)
:map evil-visual-state-map
("g a" . evil-lion-left)
("g A" . evil-lion-right)))
#+end_src
Lion sounds like /align/ … get it?
Where I like to align, is on variable assignments, e.g.
#+begin_src emacs-lisp :tangle no
(let ((foobar "Something something")
(a 42)
(very-long-var "odd string"))
;;
)
#+end_src
If you press ~RETURN~ for the /character/ to align, =evil-lion= package simply calls the built-in [[help:align][align]] function. This function chooses a regular expression based on a list of /rules/, and aligning Lisp variables requires a complicated regular expression. Extend [[elisp:(describe-variable 'align-rules-list)][align-rules-list]]:
The one thing that both Spacemacs and Doom taught me, is how much I like the /key sequences/ that begin with a leader key. In both of those systems, the key sequences begin in the /normal state/ with a space key. This means, while typing in /insert state/, I have to escape to /normal state/ and then hit the space.
I'm not trying an experiment where specially-placed function keys on my fancy ergodox keyboard can kick these off using [[https://github.com/noctuid/general.el][General Leader]] project. Essentially, I want a set of leader keys for Evil's /normal state/ as well as a global leader in all modes.
Let's try this general "space" prefix by defining some top-level operations, including hitting ~space~ twice to bring up the =M-x= collection of functions:
This is nice since the window numbers are always present on a Doom modeline, but they order the window numbers /differently/ than =ace-window=. Let's see which I end up liking better.
Ways to search for information goes under the ~s~ key. The venerable sage has always been =grep=, but we now have new-comers, like [[https://github.com/BurntSushi/ripgrep][ripgrep]], which are really fast.
**** ripgrep
Install the [[https://github.com/dajva/rg.el][rg]] package, which builds on the internal =grep= system, and creates a =*rg*= window with =compilation= mode, so ~C-j~ and ~C-k~ will move and show the results by loading those files.
I don’t understand the bug associated with the =:general= extension to =use-package=, but it /works/, but stops everything else from working, so pulling it out into its own =use-package= section addresses that issue:
The [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep package]] integrates with =ripgrep=. Typically, you hit ~i~ to automatically go into =wgrep-mode= and edit away, but since I typically want to edit everything at the same time, I have a toggle that should work as well:
Unfilling a paragraph joins all the lines in a paragraph into a single line. Taken [[http://www.emacswiki.org/UnfillParagraph][from here]] … I use this all the time:
The [[https://github.com/minad/consult][consult project]] aims to use libraries like [[*Vertico][Vertico]] to enhance specific, built-in, Emacs functions. I appreciate this project that when selecting an element in the minibuffer, it displays what you are looking at… for instance, it previews a buffer before choosing it. Unlike /Vertico/ and /Orderless/, you need to bind keys to its special functions (or rebind existing keys that do something similar).
One of the reasons that Consult hasn’t been too important to me, is that I often narrow my searching based on projectile. The [[https://gitlab.com/OlMon/consult-projectile][consult-projectile]] can help with this.
The [[https://github.com/oantolin/embark/][embark]] project offers /actions/ on /targets/. I'm primarily thinking of acting on selected items in the minibuffer, but these commands act anywhere. I need an easy-to-use keybinding that doesn't conflict. Hey, that is what the Super key is for, right?
According to [[https://elpa.gnu.org/packages/embark-consult.html#orgc76b5de][this essay]], Embark cooperates well with the [[https://github.com/minad/marginalia][Marginalia]] and [[https://github.com/minad/consult][Consult]] packages. Neither of those packages is a dependency of Embark, but Embark supplies a hook for Consult where Consult previews can be done from Embark Collect buffers:
According to the [[https://elpa.gnu.org/packages/embark-consult.html][Embark-Consult page]]:
#+begin_quote
Users of the popular [[https://github.com/justbur/emacs-which-key][which-key]] package may prefer to use the =embark-which-key-indicator= from the [[https://github.com/oantolin/embark/wiki/Additional-Configuration#use-which-key-like-a-key-menu-prompt][Embark wiki]]. Just copy its definition from the wiki into your configuration and customize the =embark-indicators= user option to exclude the mixed and verbose indicators and to include =embark-which-key-indicator=.
#+end_quote
In other words, typing ~s-;~ to call Embark, specifies the options in a buffer, but the following code puts them in a smaller configuration directly above the selections.
I often use the Emacs commands, ~M-t~ and whatnot to exchange words and whatnot, but this requires a drop out of normal state mode. The [[https://github.com/Dewdrops/evil-exchange][evil-exchange]] project attempts to do something similar, but in a VI-way.
Let’s explain how this works as the documentation assumes some previous knowledge. If you had a sentence:
The ball was red and the boy was blue.
Move the point to the word, /red/, and type ~g x i w~ (anywhere since we are using the inner text object). Next, jump to the word /blue/, and type the sequence, ~g x i w~ again, and you have:
The ball was blue and the boy was red.
The idea is that you can exchange anything. The ~g x~ marks something (like what we would normally do in /visual mode/), and then by marking something else with a ~g x~ sequence, it swaps them.
*** Evil Commentary
The [[https://github.com/linktohack/evil-commentary][evil-commentary]] is a VI-like way of commenting text. Yeah, I typically type ~M-;~ to call Emacs’ originally functionality, but in this case, ~g c c~ comments out a line(s), and ~g c~ takes text objects and whatnot. For instance, ~g c $~ comments to the end of the line.
Dropping into Emacs state is better than pure Evil state for applications, however, [[https://github.com/emacs-evil/evil-collection][the evil-collection package]] creates a hybrid between the two, that I like.
Doom introduced me to [[https://github.com/hlissner/evil-snipe][evil-snipe]], like =f= and =t=, but with two characters, and can, when configured, search more than the current line. My issue is that [[Evil Surround]] uses the same keybindings. Since surround doesn’t work in /normal/ and /visual/ states, we’ll bind snipe only for those:
I like both [[https://github.com/emacs-evil/evil-surround][evil-surround]] and Henrik's [[https://github.com/hlissner/evil-snipe][evil-snipe]], but they both start with ~s~, and conflict, and getting them to work together means I have to remember when does ~s~ call sniper and when it calls surround. As an original Emacs person, I am not bound by that key history, but I do need them consistent, so I’m choosing the ~s~ to be /surround/.
While I grew up on =Control S=, I am liking the /mental model/ associated with the [[https://github.com/abo-abo/avy][avy project]] that allows a /jump/ among matches across all visible windows. I use the ~F18~ key on my keyboard that should be easy to use, but ~g o~ seems obvious.
I originally appreciated [[https://github.com/abo-abo/ace-link][ace-link]] to work with hyperlinks on Org, EWW and Info pages, but the [[https://github.com/noctuid/link-hint.el][link-hint]] project works with more types of links:
Magnar Sveen's [[https://github.com/magnars/expand-region.el][expand-region]] project allows me to hit ~v~ in =visual= mode, and have the selection grow by syntactical units.
While editing any file on disk is easy enough, I like the mental context switch associated with a full-screen window frame showing all the buffers of a /project task/ (often a direct link to a repository project, but not always).
While I don't /need/ all the features that [[https://github.com/bbatsov/projectile][projectile]] provides, it has all the features I do need, and is easy enough to install. I am referring to the fact that I /could/ use the built-in =project.el= system (see [[https://cestlaz.github.io/post/using-emacs-79-project/][this essay]] for details on what I mean as an alternative).
A /workspace/ (at least to me) requires a quick jump to a collection of buffer windows organized around a project or task. For this, I'm basing my work on the [[https://github.com/nex3/perspective-el][perspective.el]] project.
I build a Hydra to dynamically list the current projects as well as select the project.
To do this, we need a way to generate a string of the perspectives in alphabetical order:
Let's describe a list of startup project workspaces. This way, I don't need the clutter of the recent state, but also get back to a state of mental normality.
Granted, this list is essentially a list of projects that I'm currently developing, so I expect this to change often.
Often, but not always, I want a perspective based on an actual Git repository, e.g. a project. Projectile keeps state of a "project" based on the current file loaded, so we /combine/ the two projects by first choosing from a list of /known projects/ and then creating a perspective based on the name. To pin the perspective to a project, we load a file from it, e.g. Like a README or something.
The [[https://scripter.co/using-git-delta-with-magit][magit-delta]] project uses [[https://github.com/dandavison/delta][git-delta]] for colorized diffs.
#+begin_src emacs-lisp
(use-package magit-delta
:ensure t
:hook (magit-mode . magit-delta-mode))
#+end_src
I also need to append the following to my [[file:~/.gitconfig][~/.gitconfig]] file:
I’m stealing the code for this section from [[https://tsdh.org/posts/2022-08-01-difftastic-diffing-with-magit.html][this essay]] by Tassilo Horn, and in fact, I’m going to lift a lot of his explanation too, as I may need to remind myself how this works. The idea is based on using Wilfred’s excellent [[https://github.com/Wilfred/difftastic][difftastic]] tool to do a structural/syntax comparison of code changes in git. To begin, install the binary:
#+begin_src sh
brew install difftastic # and the equivalent on Linux
#+end_src
Next, we can do this, to use this as a diff tool for everything.
#+begin_src emacs-lisp
(setenv "GIT_EXTERNAL_DIFF" "difft")
#+end_src
But perhaps integrating it into Magit and selectively calling it (as it is slow). Tassilo suggests making the call to =difft= optional by first creating a helper function to set the =GIT_EXTERNAL_DIFF= to =difft=:
#+begin_src emacs-lisp :tangle no
(defun th/magit--with-difftastic (buffer command)
"Run COMMAND with GIT_EXTERNAL_DIFF=difft then show result in BUFFER."
(let ((process-environment
(cons (concat "GIT_EXTERNAL_DIFF=difft --width="
(number-to-string (frame-width)))
process-environment)))
;; Clear the result buffer (we might regenerate a diff, e.g., for
;; the current changes in our working directory).
(with-current-buffer buffer
(setq buffer-read-only nil)
(erase-buffer))
;; Now spawn a process calling the git COMMAND.
(make-process
:name (buffer-name buffer)
:buffer buffer
:command command
;; Don't query for running processes when emacs is quit.
:noquery t
;; Show the result buffer once the process has finished.
The crucial parts of this helper function are that we "wash" the result using =ansi-color-apply-on-region= so that the function can transform the difftastic highlighting using shell escape codes to Emacs faces. Also, note the need to possibly change the width, as difftastic makes a side-by-side comparison.
The functions below depend on [[help:magit-thing-at-point][magit-thing-at-point]], and this depends on the [[https://sr.ht/~pkal/compat/][compat]] library, so let’s grab that stuff:
Next, let's define our first command basically doing a =git show= for some revision which defaults to the commit or branch at point or queries the user if there's none.
#+begin_src emacs-lisp :tangle no
(defun th/magit-show-with-difftastic (rev)
"Show the result of \"git show REV\" with GIT_EXTERNAL_DIFF=difft."
(interactive
(list (or
;; Use if given the REV variable:
(when (boundp 'rev) rev)
;; If not invoked with prefix arg, try to guess the REV from
;; point's position.
(and (not current-prefix-arg)
(or (magit-thing-at-point 'git-revision t)
(magit-branch-or-commit-at-point)))
;; Otherwise, query the user.
(magit-read-branch-or-commit "Revision"))))
(if (not rev)
(error "No revision specified")
(th/magit--with-difftastic
(get-buffer-create (concat "*git show difftastic " rev "*"))
And here the second command which basically does a =git diff=. It tries to guess what one wants to diff, e.g., when point is on the Staged changes section in a magit buffer, it will run =git diff --cached= to show a diff of all staged changes. If it can not guess the context, it'll query the user for a range or commit for diffing.
#+begin_src emacs-lisp :tangle no
(defun th/magit-diff-with-difftastic (arg)
"Show the result of \"git diff ARG\" with GIT_EXTERNAL_DIFF=difft."
(interactive
(list (or
;; Use If RANGE is given, just use it.
(when (boundp 'range) range)
;; If prefix arg is given, query the user.
(and current-prefix-arg
(magit-diff-read-range-or-commit "Range"))
;; Otherwise, auto-guess based on position of point, e.g., based on
;; if we are in the Staged or Unstaged section.
(pcase (magit-diff--dwim)
('unmerged (error "unmerged is not yet implemented"))
('unstaged nil)
('staged "--cached")
(`(stash . ,value) (error "stash is not yet implemented"))
(`(commit . ,value) (format "%s^..%s" value value))
What's left is integrating the new show and diff commands in Magit. For that purpose, Tasillo created a new transient prefix for all personal commands. Intriguing, but I have a hack that I can use on a leader:
Using the [[https://github.com/emacsmirror/gist][gist package]] to write code snippets on [[https://gist.github.com/][Github]] seems like it can be useful, but I'm not sure how often.
Every /so often/, pop over to the following URLs and generate a new token where the *Note* is =forge=, and then copy that into the [[file:~/.authinfo.gpg][~/.authinfo.gpg]]:
Pushing directly to the upstream branch is /bad form/, as one should create a pull request, etc. To prevent an accidental push, we /double-check/ first:
I primarily use [[https://github.com/jaypei/emacs-neotree][Neotree]] when I am screen-sharing my Emacs session with collegues as it shows a /project/ like an IDE.
Let's try [[https://github.com/bastibe/annotate.el][annotate-mode]], which allows you to drop "notes" and then move to them (yes, serious overlap with bookmarks, which we will return to).
Making demonstrations /within/ Emacs with my [[https://github.com/howardabrams/demo-it][demo-it]] project. While on MELPA, I want to use my own cloned version to make sure I can keep debugging it.