Emacs
Much Ado about Emacs 008
Going Over to the Dark Side1
Started reading the Emacs manual and discovered something that bugged me.
My knowledge of Emacs is bipolar. There are some advanced things that I am comfortable with and there are some basic things which I have no clue about. I know keybindings and how to set them, but I don’t know how to move around a document, the Emacs way.
Move to beginning of a line: C-a.
I knew that.
Move to the beginning of a sentence: M-a.
I did not.
Move to the end of a sentence: M-e.
Did not know this either.
I realized that the gaps in my knowledge requires me to learn a new way to engage with Emacs. This would obviously require practice. If I was going to reinvent the learning curve, why not go whole-hog? That thought lead me to evil-mode.
I have heard a lot about VIM keybindings. I don’t know them to any great extent. I am going to learn them and use them in Emacs. You can reasonably ask at this stage: “Why the fuck would you do that?”
The answer is probably:
- I like pain. (said in jest, but might be unnervingly accurate)
- I am curious. A whole host of people I respect swear by VIM keybindings. They want it everywhere. Every editor I know of has customers who want or use VIM keybindings. Hell there is a program called kindaVim which lets you use VIM keybindings almost everywhere in macOS. What is the magic that all of these folks are clamoring for? I am curious.
So, I installed evil-mode. Turned off god-mode. And a journey begins.
The learning curve just got longer/steeper.
If you break it down, keybindings in Emacs can be grouped into two categories:
- The actual editing of text.
- Dealing with Emacs.
In evil-mode, the dealing with Emacs activities are improved by using general.el. Thanks to the modal nature of evil-mode, you can have different keybindings assigned to different modes. Also spacebar as the leader key is simply fantastic.
This is my general.el configuration to deal with Emacs. None of this originated from me. I looked at a whole host of config files and came up with this for my use. It is also not set in stone, over time, I intend to customize this to fit my needs better.
* Preamble
#+BEGIN_SRC emacs-lisp
(general-define-key
:states '(normal motion visual)
:keymaps 'override
:prefix "SPC"
;; Top level functions
"." '(find-file :which-key "find file")
"," '(consult-recent-file :which-key "recent files")
"TAB" '(switch-to-prev-buffer :which-key "previous buffer")
;;"r" '(jump-to-register :which-key "registers")
"c" '(org-capture :which-key "org-capture")
#+END_SRC
** Applications
#+BEGIN_SRC emacs-lisp
;; "Applications"
"a" '(nil :which-key "applications")
"ao" '(org-agenda :which-key "org-agenda")
"ac" '(org-capture :which-key "org-capture")
"ab" '(nil :which-key "browse url")
"abc" '(browse-url-chrome :which-key "chrome")
"abg" '(my/er-google :which-key "google search")
"av" '(nil :which-key "avy")
"avc" '(avy-goto-char-timer :which-key "avy goto char")
"avp" '(avy-goto-parens :which-key "avy goto parens")
"ad" '(dired :which-key "dired")
#+END_SRC
** Buffers
#+BEGIN_SRC emacs-lisp
;; Buffers
"b" '(nil :which-key "buffer")
"bb" '(evil-switch-to-windows-last-buffer :which-key "switch buffers")
"bc" '(consult-buffer :which-key "consult-buffer")
"bd" '(evil-delete-buffer :which-key "delete buffer")
"bi" '(ibuffer :which-key "ibuffer")
"bl" '(clone-indirect-buffer :which-key "indirect buffer")
"bm" '(my/kill-other-buffers :which-key "kill other buffers")
"br" '(revert-buffer :which-key "revert buffer")
"bs" '(scratch :which-key "scratch buffer")
#+END_SRC
** Consult
#+begin_src emacs-lisp
"c" '(nil :which-key "consult")
"cd" '(consult-dir :which-key "con dir")
"cg" '(consult-grep :which-key "con grep")
"ch" 'consult-org-heading :which-key "con org heading"
"cl" '(consult-line :which-key "con line")
"cn" '(consult-notes :which-key "con notes")
"co" '(consult-outline :which-key "con outline")
"cy" '(consult-yasnippet :which-key "con yasnippet")
"cb" '(nil :which-key "bookmark/buffer")
"cbo" '(consult-bookmark :which-key "con bookmark")
"cbu" '(consult-buffer :which-key "con buffer")
;;Consult-register
"cr" '(nil :which-key "consult-register")
"crr" '(consult-register :which-key "con register")
"crs" '(consult-register-store :which-key "con reg store")
#+end_src
** Denote
#+begin_src emacs-lisp
"d" '(nil :which-key "denote")
"dl" '(denote-link :which-key "denote-link")
"dL" '(denote-add-links :which-key "denote-add-links")
"dn" '(denote :which-key "denote")
"db" '(denote-backlinks :which-key "denote-backlinks")
"dr" '(denote-rename-file :which-key "denote-rename-file")
"dR" '(denote-rename-file-using-front-matter :which-key "denote-rename-file-using-front-matter")
;; Key bindings specifically for Dired.
:map dired-mode-map
"dd" '(nil :which-key "denote-dired")
"ddl" '(denote-dired-link-marked-notes :which-key "denote-dired-link-marked-notes")
"ddr" '(denote-dired-rename-files :which-key "denote-dired-rename-files")
"ddk" '(denote-dired-rename-marked-files-with-keywords :which-key "denote rename keywords")
"ddR" '(denote-dired-rename-marked-files-using-front-matter :which-key "denote rename front matter")
#+end_src
** Elfeed
#+begin_src emacs-lisp
"e" '(nil :which-key "elfeed")
"ee" '(elfeed :which-key "elfeed")
"eu" '(elfeed-update :which-key "elfeed-update")
#+end_src
** Files
#+BEGIN_SRC emacs-lisp
;; Files
"f" '(nil :which-key "files")
"fb" '(consult-bookmark :which-key "bookmarks")
"ff" '(consult-find :which-key "find file")
"fo" '(reveal-in-osx-finder :which-key "reveal in finder")
"fO" '(my/open-buffer-file-mac :which-key "open buffer file")
"fr" '(consult-recent-file :which-key "recent files")
"fR" '(rename-file :which-key "rename file")
"fs" '(save-buffer :which-key "save buffer")
"fS" '(evil-write-all :which-key "save all buffers")
#+END_SRC
** Help/Emacs
#+BEGIN_SRC emacs-lisp
"h" '(nil :which-key "help/emacs")
"hv" '(describe-variable :which-key "des. variable")
"hb" '(describe-bindings :which-key "des. bindings")
"hM" '(describe-mode :which-key "des. mode")
"hf" '(describe-function :which-key "des. func")
"hF" '(describe-face :which-key "des. face")
"hk" '(describe-key :which-key "des. key")
"hm" '(nil :which-key "switch mode")
"hme" '(emacs-lisp-mode :which-key "elisp mode")
"hmo" '(org-mode :which-key "org mode")
"hmm" '(markdown-mode :which-key "markdown mode")
"hmt" '(text-mode :which-key "text mode")
"hp" '(nil :which-key "packages")
"hpr" '(package-refresh-contents :which-key "package-refresh")
"hpi" '(package-install :which-key "package-install")
"hpd" '(package-delete :which-key "package-delete")
#+END_SRC
** Jumps
#+begin_src emacs-lisp
;; Jumps
"j" '(nil :which-key "jumps")
"jr" '(jump-to-register :which-key "registers")
"jb" '(bookmark-jump :which-key "bookmark jump")
#+end_src
** System
#+begin_src emacs-lisp
;;System
"m" '(nil :which-key "system")
"m1" '(restart-emacs :which-key "restart emacs")
"m+" '(tab-bar-new-tab :which-key "tab new")
"m-" '(tab-close :which-key "tab close")
"mf" '(my/resources-visit :which-key "visit resources")'
"mr" '(nil :which-key "region")
"mre" '(er/expand-region :which-key "expand region")
"mrc" '(er/contract-region :which-key "contract region")
"mp" '(nil :which-key "palimpsest")
"mpb" '(palimpsest-move-region-to-bottom :which-key "pal move bottom")
"mpt" '(palimpsest-move-region-to-top :which-key "pal move top")
"mm" '(nil :which-key "mark")
"mml" '(my/mark-line :which-key "mark line")
"mmb" '(my/mark-block :which-key "mark block")
#+end_src
** Org mode
#+begin_src emacs-lisp
;; Org-mode
"mo" '(nil :which-key "org-mode")
"moa" '(org-agenda :which-key "org-agenda")
"moc" '(org-capture :which-key "org-capture")
"mot" '(org-todo :which-key "DONE")
"mou" '(my/org-jump-to-heading-beginning :which-key "jump to heading")
;; More Org
"mol" '(nil :which-key "line/link")
"molb" '(org-beginning-of-line :which-key "begin of line")
"mole" '(org-end-of-line :which-key "end of line")
"moli" '(my/org-insert-link-dwim :which-key "org insert link")
"mols" '(org-store-link :which-key "org store link")
;; Yet More org
"mo," '(nil :which-key "more-org")
"mo,c" '(org-cycle :which-key "org-cycle")
"mo,n" '(outline-next-visible-heading :which-key "next-heading")
"mo,p" '(outline-previous-visible-heading :which-key "previous-heading")
"mo,t" '(org-set-tags-command :which-key "set-tags")
"mo,u" '(outline-up-heading :which-key "up-heading")
#+end_src
** Registers/bookmarks
#+begin_src emacs-lisp
"r" '(nil :which-key "registers/marks")
"rr" '(consult-register :which-key "registers")
"rm" '(consult-mark :which-key "marks")
"rb" '(consult-bookmark :which-key "bookmarks")
"rs" '(bookmark-set :which-key "set bookmark")
#+end_src
** Text
#+BEGIN_SRC emacs-lisp
"x" '(nil :which-key "text")
"xC" '(my/copy-whole-buffer-to-clipboard :which-key "copy whole buffer to clipboard")
"xr" '(isearch-query-replace :which-key "find and replace")
"xs" '(yas-insert-snippet :which-key "insert yasnippet")
;;"xf" '(flush-lines :which-key "flush-lines")
"xR" '(replace-regexp :which-key "replace-regexp")
#+END_SRC
** Toggles/Visuals
#+BEGIN_SRC emacs-lisp
;; Toggles
"t" '(nil :which-key "toggles")
"tt" '(toggle-truncate-lines :which-key "truncate lines")
"tv" '(visual-line-mode :which-key "visual line mode")
"tn" '(display-line-numbers-mode :which-key "display line numbers")
"ta" '(mixed-pitch-mode :which-key "variable pitch mode")
"ty" '(consult-theme :which-key "load theme")
"tR" '(read-only-mode :which-key "read only mode")
#+END_SRC
** Windows
#+BEGIN_SRC emacs-lisp
;; Windows
"w" '(nil :which-key "window")
"wN" '(make-frame :which-key "make frame")
"wd" '(evil-window-delete :which-key "delete window")
"wb" '(vsplit-last-buffer :which-key "split below")
"wr" '(hsplit-last-buffer :which-key "split right")
"wl" '(evil-window-right :which-key "evil-window-right")
"wh" '(evil-window-left :which-key "evil-window-left")
"wj" '(evil-window-down :which-key "evil-window-down")
"wk" '(evil-window-up :which-key "evil-window-up")
"wz" '(text-scale-adjust :which-key "text zoom")
"wq" '(my/quick-window-jump :which-key "quick window jump"))
;; End SPC prefix block
#+END_SRC
** All-mode keybindings
Below are general keybindings for the various Evil modes.
#+BEGIN_SRC emacs-lisp
;; All-mode keymaps
(general-def
:keymaps 'override
;; Emacs --------
"ß" '(evil-window-next :which-key "evil-window-next") ;; option-s
"Í" '(other-frame :which-key "other-frame") ;; option-shift-s
"C-S-B" '(switch-to-buffer :which-key "switch-to-buffer")
"∫" '(switch-to-next-buffer :which-key "switch-to-next-buffer") ;; option-b
"s-b" '(switch-to-prev-buffer :which-key "switch-to-previous-buffer") ;; super-b
;; Remapping normal help features to use helpful version
"C-h v" '(helpful-variable :which-key "helpful-variable")
"C-h x" '(helpful-command :which-key "helpful-command")
"C-h f" '(helpful-function :which-key "helpful-function")
"C-h k" '(helpful-key :which-key "helpful-key")
;; Editing ------
"M-v" '(simpleclip-paste :which-key "sc-paste")
"M-V" '(evil-paste-after :which-key "evil-paste-after") ;; shift-paste uses the internal clipboard
"M-c" '(simpleclip-copy :which-key "sc-copy")
;;"M-u" '(capitalize-dwim :which-key "capitalize-dwim") ;; Default is upcase-dwim
;;"M-U" '(upcase-dwim :which-key "upcase-dwim") ;; M-S-u (switch upcase and capitalize)
;; Utility ------
"C-c c" '(org-capture :which-key "org-capture")
"C-c a" '(org-agenda :which-key "org-agenda")
"M-=" '(count-words :which-key "count-words")
"C-'" '(avy-goto-char-2 :which-key "avy-goto-char"))
#+END_SRC
#+begin_src emacs-lisp
(general-def
:states '(normal visual motion)
"gc" '(comment-dwim :which-key "comment-dwim")
"gC" '(comment-line :which-key "comment-line")
"u" '(undo-fu-only-undo :which-key "undo-fu-only-undo")
"U" '(undo-fu-only-redo :which-key "undo-fu-only-redo")
"j" '(evil-next-visual-line :which-key "evil-next-visual-line") ;; I prefer visual line navigation
"k" '(evil-previous-visual-line :which-key "evil-previous-line")
"f" '(evil-avy-goto-char-in-line :which-key "evil-avy-goto-char-in-line"))
#+end_src
** Insert mode keymaps
#+BEGIN_SRC emacs-lisp
;; Insert keymaps
;; Many of these are emulating standard Emacs bindings in Evil insert mode, such as C-a, or C-e.
(general-def
:states '(insert)
"C-a" '(evil-beginning-of-visual-line)
"C-e" '(evil-end-of-visual-line)
"C-S-a" '(evil-beginning-of-line)
"C-S-e" '(evil-end-of-line)
"C-n" '(evil-next-visual-line)
"C-p" '(evil-previous-visual-line)
)
#+END_SRC
** Emacs mode keymaps
#+BEGIN_SRC emacs-lisp
(general-def
:keymaps 'emacs
"C-w C-q" '(kill-this-buffer))
#+END_SRC
Phew. That is a lot. But it is something that I am working on fusing into my muscle memory. Wish me luck.
Speed Dial
I came across this Speed Dialing Your Favorite Files. I remembered that I used to use a similar system in iA Writer and Sublime Text. Glad to have this in Emacs.
I noticed that the Reddit comments on the post included suggestions to use registers or bookmarks instead of this abomination. I am sure that registers and bookmarks are great, but I like this better for getting to a list of ten files which get used a lot on my machine.
#+begin_src emacs-lisp
;;
;; Speed Dialing Favorite Files
;;
(defvar /speed-dial-list
'(("⓵-Emacs" . "~/.emacs.d/Emacs.org")
("⓶-todo" . "~/Dropbox/org/todo.org")
("⓷-cheat-sheet" . "~/Dropbox/org/emacs_cheat_sheet.org")
("⓸-em-man" . "~/Dropbox/org/20250118T230705--emacs-mini-manual-part-1__emacs.org")
("⓹-omguide" . "~/Dropbox/org/20250214T005052--emacs-org-mode-guide__emacs.org")
("⓺-diary" . "~/Dropbox/org/diary2025.org")
("⓻-init" . "~/.emacs.d/init.el")
("⓼-me" . "~/Dropbox/org/mastering_emacs_2024.org")
("⓽-CD" . "~/.emacs.d/Config Deletions.org")
("⓾-CB" . "~/Dropbox/org/emacs-config bits.org"))
"List of speed-dial entries as (LABEL . FILENAME).")
;; Global keybindings for speed dialing using '<Super>' + digit
(let ((i 1))
(dolist (entry /speed-dial-list)
(keymap-global-set (format "s-%d" (mod i 10))
`(lambda() (interactive) (find-file-existing ,(cdr entry))))
(setq i (1+ i))))
#+end_src
This makes accessing some of my files easier. I am sure that there are other ways of achieving this in Emacs. I like this one because I am used to it.
Emojis
I don’t use emojis much in my writing. But this makes it easy to use them on macOS if I feel so inclined. macOS Native Emoji Picking in Emacs from the Edit Menu
#+begin_src emacs-lisp
(easy-menu-add-item global-map '(menu-bar edit)
["Emoji & Symbols"
ns-do-show-character-palette
:help "Show macOS Character Palette."
:visible (eq window-system 'ns)])
#+end_src
Assorted Bits
I loved reading this My 10 Years with Emacs Plain DrOps. I wish to write about my experiences with Emacs ten years from now.
macosxguru at the gmail thingie.
Thank you Howard.↩︎