A literate programming file for configuring Emacs.
#+BEGIN_SRC emacs-lisp :exports none
;;; ha-config.org --- A literate programming file for configuring Emacs. -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2020-2021 Howard X. Abrams
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
;; Maintainer: Howard X. Abrams <howard.abrams@gmail.com>
;; Created: September 10, 2020
;;
;; This file is not part of GNU Emacs.
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; ~/other/hamacs/ha-config.org
;; Using `find-file-at-point', and tangle the file to recreate this one .
;;
;;; Code:
#+END_SRC
* Introduction
The following are general variable settings, and doesn't really belong to any specific purpose.
#+BEGIN_SRC emacs-lisp
(setq user-full-name "Howard X. Abrams"
user-mail-address "howard.abrams@gmail.com")
#+END_SRC
New way to display line-numbers. I set mine to =relative= so that I can easily jump up and down by that value. Set this to =nil= to turn off, or =t= to be absolute.
#+BEGIN_SRC emacs-lisp
(setq display-line-numbers t)
(setq display-line-numbers-type 'relative)
#+END_SRC
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.
#+BEGIN_SRC emacs-lisp
(setq auth-sources '("~/.authinfo.gpg")
auth-source-cache-expiry nil)
#+END_SRC
More settings:
#+BEGIN_SRC emacs-lisp
(setq truncate-string-ellipsis "…" ; Unicode ellispis are nicer than "..."
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:
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 maybe simpler?
I like being able to enable local variables in =.dir-local.el= files:
#+BEGIN_SRC emacs-lisp
(setq enable-local-variables t)
#+END_SRC
** Completing Read User Interface
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.
*** Vertico
The [[https://github.com/minad/vertico][vertico]] package puts the completing read in a vertical format, and seems to fit the bill. It seems to be similar to [[https://github.com/raxod502/selectrum#vertico][Selectrum]], and I'll use it (at least for a while), however, I may be jumping between the two.
#+BEGIN_SRC emacs-lisp
(use-package vertico
:config (vertico-mode))
#+END_SRC
My only issue with using Vertico with =find-file= is that I really like having the Return key insert the directory at point, and not open =dired=. Seems like this is addressed with this extension /installed with Vertico/:
While I've been /dabbling/ in some of the alternates for =completing-read=, after watching [[https://youtu.be/lfgQC540sNM][Rari Comninos' overview]], I decided to try [[https://github.com/raxod502/selectrum][selectrum]] for better narrowing and selecting (instead of Ivy) and [[https://github.com/raxod502/prescient.el][prescient]] to order the selection from history.
#+BEGIN_SRC emacs-lisp :tangle no
(use-package selectrum
:config
;; Optional performance optimization by highlighting only the visible candidates.
*Note:* Multiple files can be opened at once with =find-file= if you enter a wildcard. We may also give the =initials= completion style a try.
*** Savehist
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.
#+BEGIN_SRC emacs-lisp
(use-package savehist
:init
(savehist-mode))
#+END_SRC
*** Marginalia
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.
#+BEGIN_SRC emacs-lisp
;; Enable richer annotations using the Marginalia package
(use-package marginalia
:init
(setq marginalia-annotators-heavy t)
:config
(marginalia-mode))
#+END_SRC
* Key Bindings
To begin my binding changes, let's turn on [[https://github.com/justbur/emacs-which-key][which-key]]:
Magnar Sveen's [[https://github.com/magnars/expand-region.el][expand-region]] project allows me to hit ~v~ repeatedly, having the selection grow by syntactical units.
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.
#+BEGIN_SRC emacs-lisp
(use-package evil-collection
:after evil
:config
(evil-collection-init))
#+END_SRC
Do I want to specify the list of modes to change for =evil-collection-init=, e.g.
#+BEGIN_SRC emacs-lisp :tangle no :eval no
'(eww magit dired notmuch term wdired)
#+END_SRC
** General Leader Key Sequences
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:
"q Q" '("quit without saving" . evil-quit-all-with-error-code))
#+END_SRC
*** File Operations
Obviously, =find-file= is still my bread and butter, but I do like getting information about the file associated with the buffer. For instance, the file path:
#+BEGIN_SRC emacs-lisp
(defun ha/relative-filepath (filepath)
"Return the FILEPATH without the HOME directory and typical filing locations.
The expectation is that this will return a filepath with the proejct name."
Ways to search for information goes under the ~s~ key. This primarily depends on 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.
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:
#+BEGIN_SRC emacs-lisp
(defun unfill-paragraph ()
"Convert a multi-line paragraph into a single line of text."
The [[https://github.com/minad/consult][consult]] package is a replacement for selecting buffers and other /speciality functions/, similar to the [[https://oremacs.com/2015/04/09/counsel-completion/][Ivy's counsel completion]] project. I think I may be adding it sparingly, as personally, I read files and buffers based on the selected /project/.
The pattern is to add the /consult/ functions to my standard general leader organization, but they will all end with ~TAB~ (unique, easy and consistent).
#+BEGIN_SRC emacs-lisp
(use-package consult
:config
(ha-leader
"b TAB" '("consult buffer" . consult-buffer)
"b S-TAB" '("consult buffer in window" . consult-buffer-other-window)
"s TAB" '("consult search" . consult-ripgrep)
"f TAB" '("consult file" . consult-file)))
#+END_SRC
*** Embark
The [[https://github.com/oantolin/embark/][embark]] project offers /actions/ on /targets/, however, I'm primarily thinking of acting on selected items in the minibuffer, however, they actually act anywhere. Consequently, I need an easy-to-use keybinding that doesn't conflict. Hey, that is what the Super key is for, right?
#+BEGIN_SRC emacs-lisp
(use-package embark
:bind
(("s-;" . embark-act) ; Work in minibuffer and elsewhere
("s-/" . embark-dwim))
:init
;; Optionally replace the key help with a completing-read interface
Doom introduced me to [[https://github.com/hlissner/evil-snipe][evil-snipe]] which is similar to =f= and =t=, but does two characters, and can, when configured, search more than the current line:
It highlights all potential matches, use ~;~ to skip to the next match, and ~,~ to jump back.
** Evil Surround
I like both [[https://github.com/emacs-evil/evil-surround][evil-surround]] and Henrik's [[https://github.com/hlissner/evil-snipe][evil-snipe]], however, 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 calls surround. As an original Emacs person, I am not bound by that key history, but I do need them consistent:
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.
*Note:* The links should be shorter near the point as opposed to starting from the top of the window.
** Miscellaneous Keys
I really appreciated the [[https://github.com/benma/visual-regexp.el][visual-regexp package]]:
#+BEGIN_SRC emacs-lisp
(use-package visual-regexp
:bind (("C-c r" . vr/replace)
("C-c q" . vr/query-replace)))
#+END_SRC
* Working Layout
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).
** Projects
While I really 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:
#+BEGIN_SRC emacs-lisp
(defun ha--persp-label (num names)
"Return string of numbered elements. NUM is the starting
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 just need to load a file from it, e.g. Like a README or something.
Displaying a few files? Well, when /starting/ I am only showing one or two files (maybe three), so we will split the window horizontally for each file.
#+BEGIN_SRC emacs-lisp
(defun ha--project-show-files (root files)
"Display a list of FILES in a project ROOT directory.
Each file gets its own window (so don't make the list of files
long)."
(message "Loading files from %s ... %s" root files)
(let* ((file (car files))
(more (cdr files))
(filename (format "%s/%s" root file)))
(find-file filename)
(when more
(split-window-horizontally)
(ha--project-show-files root more))))
#+END_SRC
* Applications
Can we really call these /applications/?
** Magit
Can not live without [[https://magit.vc/][Magit]], a Git porcelain for Emacs. I stole the bulk of this work from Doom Emacs.
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.
I'm not giving up on Eshell, but I am playing around with [[https://github.com/akermu/emacs-libvterm][vterm]], and it is pretty good, but I use it primarily as a more reliable approach to [[file:ha-remoting.org][a remote shell]].
VTerm has an issue (at least for me) with ~M-Backspace~ not deleting the previous word, and yeah, I want to make sure that both keystrokes do the same thing.
The advantage of running terminals in Emacs is the ability to copy text without a mouse. For that, hit ~C-c C-t~ to enter a special copy-mode. If I go into this mode, I might as well also go into normal mode to move the cursor.
*Note:* To exit the copy-mode (and copy the selected text to the clipboard), hit ~Return~.
Hrm. Seems that I might want a function to copy the output of the last command to a register, or even an org-capture...
* Technical Artifacts :noexport:
Let's provide a name so that the file can be required:
#+BEGIN_SRC emacs-lisp :exports none
(provide 'ha-config)
;;; ha-config.el ends here
#+END_SRC
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
#+DESCRIPTION: A literate programming file for configuring Emacs.
#+PROPERTY: header-args:sh :tangle no
#+PROPERTY: header-args:emacs-lisp :tangle yes
#+PROPERTY: header-args :results none :eval no-export :comments no