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.
Can’t remember all the shortcuts on the ~g~ key, and =which-key= displays the entire function, so let’s /re-add/ those keybindings, but with labels. The ~g~ is extemely convenient, yet I realize that I will never use some of the default keybindings (like ~g m~ to go to the middle of the line? Too imprecise). So I am also going to delete some of them.
#+begin_src emacs-lisp
(use-package evil
:general
(:states '(normal visual motion operator)
;; These go into operator mode, so the key sequence, g U i o
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:
"f F" '("load new window" . find-file-other-window)
"f l" '("locate" . locate)
"f s" '("save" . save-buffer)
"f S" '("save as" . write-buffer)
"f r" '("recent" . recentf-open-files)
"f c" '("copy" . copy-file)
"f R" '("rename" . rename-file)
"f D" '("delete" . delete-file)
"f y" '("yank path" . ha-yank-buffer-path)
"f Y" '("yank path from project" . ha-yank-project-buffer-path)
"f d" '("dired" . dirvish)
"f 1" '("load win-1" . ha-find-file-window-1)
"f 2" '("load win-2" . ha-find-file-window-2)
"f 3" '("load win-3" . ha-find-file-window-3)
"f 4" '("load win-4" . ha-find-file-window-4)
"f 5" '("load win-5" . ha-find-file-window-5)
"f 6" '("load win-6" . ha-find-file-window-6)
"f 7" '("load win-7" . ha-find-file-window-7)
"f 8" '("load win-8" . ha-find-file-window-8)
"f 9" '("load win-9" . ha-find-file-window-9))
#+end_src
On Unix systems, the =locate= command is faster than =find= when searching the whole system, since it uses a pre-computed database, and =find= is faster if you need to search a specific directory instead of the whole system. On the Mac, we need to change the =locate= command:
#+begin_src emacs-lisp
(when (ha-running-on-macos?)
(setq locate-command "mdfind"))
#+end_src
The advantage of =mdfind= is that is searches for filename /and/ its contents of your search string.
Trying the [[https://github.com/benmaughan/spotlight.el][spotlight]] project, as it has a slick interface for selecting files:
I like the idea of dropping returnable bookmarks, however, the built-in behavior doesn’t honor either /projects/ or /perspectives/, but I use [[https://codeberg.org/ideasman42/emacs-bookmark-in-project][bookmark-in-project]] package to make a =project=-specific bookmarks and use that to jump to only bookmarks in the current project.
While it comes with Emacs, I use [[https://www.emacswiki.org/emacs/WinnerMode][winner-mode]] to undo window-related changes:
#+begin_src emacs-lisp
(use-package winner
:custom
(winner-dont-bind-my-keys t)
:config
(winner-mode +1))
#+end_src
*** Ace Window
Use the [[https://github.com/abo-abo/ace-window][ace-window]] project to jump to any window you see.
Often transient buffers show in other windows, obscuring my carefully crafted display. Instead of jumping into a window, typing ~q~ (to either call [[help:quit-buffer][quit-buffer]]) if available, or [[help:bury-buffer][bury-buffer]] otherwise. This function hooks to =ace-window=
#+begin_src emacs-lisp
(defun ha-quit-buffer (window)
"Quit or bury buffer in a given WINDOW."
(interactive)
(aw-switch-to-window window)
(unwind-protect
(condition-case nil
(quit-buffer)
(error
(bury-buffer))))
(aw-flip-window))
#+end_src
Since I use numbers for the window, I can make the commands more mnemonic, and add my own:
Keep in mind, these shortcuts work with more than two windows open. For instance, ~SPC w w d 3~ closes the "3" window.
*** Transpose Windows
My office at work has a monitor oriented vertically, and to move an Emacs with “three columned format” to a “stacked format” I use the [[https://www.emacswiki.org/emacs/TransposeFrame][transpose-frame]] package:
#+begin_src emacs-lisp
(use-package transpose-frame)
#+end_src
*** Winum
To jump to a window even quicker, use the [[https://github.com/deb0ch/emacs-winum][winum package]]:
#+begin_src emacs-lisp
(use-package winum
:bind (("s-1" . winum-select-window-1)
("s-2" . winum-select-window-2)
("s-3" . winum-select-window-3)
("s-4" . winum-select-window-4)
("s-5" . winum-select-window-5)
("s-6" . winum-select-window-6)
("s-7" . winum-select-window-7)
("s-8" . winum-select-window-8)
("s-9" . winum-select-window-9)))
#+end_src
This is nice since the window numbers are always present on a Doom modeline, but they sometime order the window numbers /differently/ than =ace-window=.
The ~0~ key/window should be always associated with a project-specific tree window of =dired= (or [[Dirvish][Dirvish]]):
This means that, without thinking, the following just works:
- ~SPC w s s s~ :: creates a window directly below this.
- ~SPC w n n n~ :: creates a window directly to the right.
But, more importantly, the prefix ~w s~ gives me more precision to view what I need.
** Search Operations
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.
#+begin_src emacs-lisp
(use-package rg
:config
;; Make an interesting Magit-like menu of options, which I don't use much:
Note we bind the key ~M-R~ to the [[help:rg-menu][rg-menu]], which is a Magit-like interface to =ripgrep=.
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:
#+begin_src emacs-lisp
(use-package rg
:general (:states 'normal "gS" 'rg-dwim))
#+end_src
*** wgrep
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:
#+begin_src emacs-lisp
(defun unfill-paragraph ()
"Convert a multi-line paragraph into a single line of text."
(interactive)
(let ((fill-column (point-max)))
(fill-paragraph nil)))
#+end_src
** Help Operations
While the ~C-h~ is easy enough, I am now in the habit of typing ~SPC h~ instead.
Since I tweaked the help menu, I craft my own menu:
;; Since I do a lot of literate programming, I appreciate a quick
;; jump directly into the Info manual...
"h B" '("org babel" . (lambda () (interactive)
(org-info-open "org#Working with Source Code" nil))))
#+end_src
Remember these keys in the *Help* buffer:
- ~s~ :: view source of the function
- ~i~ :: view info manual of the function
Let's make Info behave a little more VI-like:
#+begin_src emacs-lisp
(use-package info
:straight (:type built-in)
:general
(:states 'normal :keymaps 'Info-mode-map
"B" 'Info-bookmark-jump
"Y" 'org-store-link
"H" 'Info-history-back
"L" 'Info-history-forward
"u" 'Info-up
"U" 'Info-directory
"T" 'Info-top-node
"p" 'Info-backward-node
"n" 'Info-forward-node)) ; Old habit die hard
#+end_src
** Consult
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).
If found the =consult-mark= as part of [[https://arialdomartini.github.io/emacs-mark-ring][this essay]] about the =mark=.
An under-appreciated version of Consult is the /changing your mind/ aspect. Type ~SPC b b~ to switch to a different buffer, and change your mind, “oh, I really need a file!” Type ~f SPC~ and it switches to a file browser. Nope, I did need the buffer, type ~b SPC~ and your back to buffer switching. Other /narrowing/ keys:
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?
#+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
In [[https://karthinks.com/software/fifteen-ways-to-use-embark/][15 Ways to Use Embark]], Karthik Chikmagalur suggests a nifty macro for integrating Embark with [[Ace Window][Ace Window]]:
We can rebind the various =embark-xyz-map= with calls to our macroized functions:
#+begin_src emacs-lisp
(use-package embark
:bind
(:map embark-file-map
("y" . embark-copy-as-kill)
("Y" . embark-save-relative-path)
("W" . nil)
("w" . my/embark-ace-find-file)
("2" . my/embark-find-file-below)
("3" . my/embark-find-file-right)
:map embark-buffer-map
("y" . embark-copy-as-kill)
("w" . my/embark-ace-switch-to-buffer)
("2" . my/embark-switch-to-buffer-below)
("3" . my/embark-switch-to-buffer-right)
:map embark-file-map
("y" . embark-copy-as-kill)
("w" . my/embark-ace-bookmark-jump)
("2" . my/embark-bookmark-jump-below)
("3" . my/embark-bookmark-jump-right)))
#+end_src
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:
#+begin_src emacs-lisp
(use-package embark-consult
:after (embark consult)
:demand t ; only necessary if you have the hook below
;; if you want to have consult previews as you move around an
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.
#+begin_src emacs-lisp
(defun embark-which-key-indicator ()
"An embark indicator that displays keymaps using which-key.
The which-key help message will show the type and value of the
current target followed by an ellipsis if there are further