home

init.el

Table of Contents

1. overview

1.1. purpose

Documentation for my emacs config. It provides the actual configuration as well as useful context like how the configuration works or relavent documentation.

The configuration itself follows three principles:

  • Portable: This config should work on any machine whether its ran in a terminal or gui.
  • Minimal: This config is small enough to understand fully.
  • Useful: 95% of what you need to be effective with emacs is provided.

1.2. installing

Simply run org-babel-tangle to export the code in this file to your init file.

1.3. links

This config, and a lot of the org mode settings, was inspired by Org Mode - Organize Your Life In Plain Text! A great read if you want to learn more about configuring org mode.

For more emacs inspiration check out Rougier on Github. He's doing some of the best UI/UX work in emacs at the moment.

Another great read is Literate DevOps which shows the power of org source blocks.

2. init.el

2.1. global changes

A catch all for settings that work across all emacs modes.

(setq default-directory "~/"
      ring-bell-function 'ignore
      tab-always-indent 'complete            ;; Tab indents a line. If the line is indented then it runs completion-at-point
      help-window-select t                   ;; Automatically focus on help window when they pop up
      split-width-threshold nil              ;; Split windows vertically
      custom-file "~/.emacs.d/custom.el"     ;; Contains customized variables
      x-super-keysym 'meta                   ;; Use command key on mac as meta
      compilation-scroll-output 'first-error ;; Automatically scroll to first error in a completion buffer
      )

(add-to-list 'exec-path "/usr/local/bin/")
(global-visual-line-mode 1)
(savehist-mode) ;; save shell history
(defalias 'yes-or-no-p 'y-or-n-p) ;; don't ask for yes/no as for y/n
(add-hook 'compilation-finish-functions 'switch-to-buffer-other-window 'compilation)

2.2. backups

Sensible settings for emacs backups. All backups and autosaves are put in the ~/.emacs.d/ folder so temp files don't pollute the filesystem.

(thoughts on other ways to manage backups)

(setq backup-directory-alist `(("." . ,(concat user-emacs-directory "backup")))
      backup-by-copying      t  ;; Don't de-link hard links
      version-control        t  ;; Use version numbers on backups
      delete-old-versions    t  ;; Automatically delete excess backups:
      kept-new-versions      20 ;; how many of the newest versions to keep
      kept-old-versions      5) ;; and how many of the old
(unless (file-directory-p "~/.emacs.d/auto-save")
  (make-directory "~/.emacs.d/auto-save"))
(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save/" t)))

2.3. elisp

A couple of convenience functions to make elisp more readable.

(defun first  (list) "The first item in the list (car)" (nth 0 list))
(defun second (list) "The second item in the list" (nth 1 list))
(defun rest   (list) "All items in a list after the first (cdr)" (cdr list))

2.4. helpers

2.4.1. last command

I use this as my main command runner. The first time it's run it opens M-x. After that it cycles through the previous commands you run. So, if you run this twice it will show the last command you ran. I have it bound to C-; in my keybindings.

(defun execute-last-command ()
  "Executes the last extended command that was run"
  (interactive)
  (let* ((cmd-str (first extended-command-history))
         (cmd (intern-soft cmd-str)))
    (message (concat "Executing: `" cmd-str "`"))
    (command-execute cmd)))

(defun execute-command-or-search-history ()
  (interactive)
  (call-interactively (if (minibufferp)
                          'previous-history-element
                        'execute-extended-command)))

2.4.2. tangle helpers

I like to keep my configs in org files. The functions below find those files and tangle the output.

(defun tangle-init ()
  (interactive)
  (save-window-excursion
    (find-file "~/org/projects/emacs.org")    
    (widen)
    (beginning-of-buffer)
    (re-search-forward "* init")
    (org-narrow-to-subtree)
    (org-babel-tangle)
    (widen)))

(defun tangle-snippets ()
  (interactive)
  (save-window-excursion
    (find-file "~/org/projects/emacs.org")
    (widen)
    (end-of-buffer)
    ;; Backwards because searching forwards will find this line
    (re-search-backward "* Snippets")
    (org-narrow-to-subtree)
    (org-babel-tangle)
    (widen)))

(defun tangle-dot-files ()
  (interactive)
  (save-window-excursion
    (find-file "~/org/projects/dotfiles.org")
    (org-babel-tangle)))

2.4.3. split-region

(defun split-region (del rep)
  "Spilts a region by the given delimiter and replaces it with the given rep"
  (interactive
   (list (read-string "delimeter: " "")
         (read-string "replace: " "\n")))
  (let ((s (buffer-substring-no-properties (region-beginning) (region-end))))
    (delete-region (region-beginning) (region-end))
    (insert (replace-regexp-in-string del rep s t t))))

2.4.4. align-regex (with space)

(defadvice align-regexp (around align-regexp-with-spaces activate)
  (let ((indent-tabs-mode nil))
    ad-do-it))

2.4.5. kill-processes

(defun kill-processes (regex)
  "Kill all processes whose names match the given regex."
  (interactive "sEnter regex to match processes: ")
  (dolist (process (process-list))
    (let ((name (process-name process)))
      (when (and name (string-match-p "nrepl" name))
        (delete-process process)
        (message "Killed process: %s" name)))))

2.4.6. launch

(defun insert-return ()
  "Presses the RETURN key"
  (funcall (lookup-key (current-local-map) (kbd "RET"))))

(defun launch (cmd)
  "Opens a shell and runs the given command. Useful for running long running commands from emacs."
  (interactive "sCommand to run: ")
  (let* ((shell-name (format "*Launch: %s*" (truncate-string-to-width cmd 10)))
         (buffer (shell shell-name)))
    (sit-for 1)
    (insert cmd)
    (insert-return)))

2.4.7. list directories

Recursively list all directories under a directory

(defun list-directories-recursively (directory)
  "List all directories recursively in DIRECTORY."
  (let ((directories (list directory)))
    (dolist (file (directory-files-recursively directory "" t))
      (when (file-directory-p file)
        (push file directories)))
    directories))

2.5. keybindings

(defun global-set-keys (key-action-pairs)
  "Binds a list of keybindings in the form (keybinding action)"
  (when key-action-pairs
    (let ((key (first (first key-action-pairs)))
          (action (second (first  key-action-pairs))))
      (global-set-key (kbd key) action))
    (global-set-keys (cdr key-action-pairs))))

(global-set-keys
 '(;; Emacs
   ("<C-wheel-down>"  ignore)
   ("<C-wheel-up>"    ignore)
   ("C-;"             execute-command-or-search-history)
   ("M-^"             execute-command-or-search-history) ; set ctrl-; to ^; iterm
   ("C-'"             previous-history-element)
   ("C-S-<right>"     enlarge-window-horizontally)
   ("C-S-<left>"      shrink-window-horizontally)
   ("C-S-<up>"        shrink-window)
   ("C-S-<down>"      enlarge-window)
   ("C-x ,"           rename-buffer)
   ;; Navigation
   ("M-,"             pop-global-mark)
   ("C-x e"           end-of-buffer)
   ("C-c C-e"         eval-defun)
   ("C-x t"           beginning-of-buffer)
   ("C-x i"           info)
   ("C-x a"           align-regexp)
   ("C-x C-g"         goto-line)
   ("C-c r"           replace-regexp)
   ;; Editing
   ("C-x y"           (lambda () (interactive) (popup-menu 'yank-menu)))
   ("M-w"             backward-kill-word)
   ("C-x q"           kmacro-end-and-call-macro)
   ("C-x c"           comment-line)
   ;; Actions
   ("C-c b"           compile)
   ("C-c C-b"         (lambda () (interactive) (save-buffer) (recompile)))
   ("C-c t"           (lambda () (interactive) (print "Missing test runner for this major mode.")))
   ("M-."             xref-find-definitions)
   ("C-c D"           (lambda () (interactive) (print "Missing disassembler for this major mode.")))
   ("C-c d"           (lambda () (interactive) (print "Missing debugger for this major mode.")))   
   ("C-c f"           (lambda () (interactive) (print "Missing formatter for this major mode.")))
   ("C-c h"           (lambda () (interactive) (print "Missing help for this major mode.")))
   ("C-h d"           (lambda () (interactive) (call-interactively 'man)))
   ))

2.6. package managers

Two package managers are used:

  1. straight

    A package manager with the ability to pull from github.

  2. use-package

    A package manager with a great interface. Straight is setup to use this as a front end.

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 6))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

;; Use Package
(straight-use-package 'use-package)
(setq straight-use-package-by-default t)
;; Profile startup time
(require 'use-package)
(setq use-package-always-defer t)
(setq use-package-compute-statistics t)

2.7. ui

2.7.1. nano

Github

A fantastic UI for emacs. I highly recommend checking out Rougier's other emacs work.

Nano emacs overrides a lot of defaults. I like to run it as early as possible so it doesn't override some of the changes I make.
(when (window-system)
  (straight-use-package
   '(nano-emacs :type git :host github :repo "rougier/nano-emacs"))
  ;; Need to setq before calling nano-faces
  (setq nano-font-family-monospaced "Roboto Mono"
        nano-font-size 16)

  (require 'nano-theme-light)
  (nano-theme-set-light)
  (require 'nano-colors)
  ;;(require 'nano-minibuffer)
  (require 'nano-faces)
  (nano-faces)

  (require 'nano-theme)
  (nano-theme)

  (require 'nano-modeline))

;; Hack to override nano custom show paren
(set-face-foreground 'show-paren-match "#00f")
(set-face-attribute 'show-paren-match nil :weight 'extra-bold)
(set-face-attribute 'font-lock-string-face nil
                    :foreground "indian red")
(set-face-attribute 'region nil
                    :background "#D8D9DA")

2.7.2. gui

Custom UI changes when running in the gui.

(when (window-system)
  (setq ns-use-native-fullscreen t
        mac-command-modifier 'meta
        mac-option-modifier 'meta
        mac-use-title-bar nil)
  (progn
    (tool-bar-mode 0)
    (menu-bar-mode -1)
    (scroll-bar-mode -1)
    (set-face-attribute 'default nil
                        :font "Roboto Mono"
                        :height 160)))

2.7.3. terminal

Custom UI changes when running in a terminal.

(when (not (window-system))
  (progn
    (load-theme 'tsdh-dark t)
    (menu-bar-mode -1)
    (set-frame-font "Roboto Mono 16")
    (display-battery-mode 1)))

2.8. packages

2.8.1. ace window

Github

Makes switching windows easier.

(use-package ace-window
  :bind ("C-x o" . ace-window)
  :init (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
  (setf aw-dispatch-alist '()))

2.8.2. auto-yasnippet

Github

Inline snippets for on the fly templating.

(use-package auto-yasnippet
  :bind
  ("M-w" . #'aya-create)
  ("M-y" . #'aya-expand)
  :init (setq aya-marker "$"))

2.8.3. avy

Github

Jump to anywhere in a buffer by typing the text of the location you want to jump to.

(use-package avy
  :bind
  ("C-o" . avy-goto-char-timer))

2.8.4. c

(require 'cc-mode)
(define-key c-mode-base-map (kbd "C-c C-b") nil)
(define-key c-mode-base-map (kbd "C-c d") 'disaster)

2.8.5. c++

(defun my-c++-mode-hook ()
    (setq c-basic-offset 4)
    (c-set-offset 'substatement-open 0)
    (flycheck-mode))
(add-hook 'c++-mode-hook 'my-c++-mode-hook)
(define-key c++-mode-map (kbd "C-c C-b") nil)

2.8.6. cider

Docs

Clojure IDE for interactive development.

(defun cider-smart-run-tests ()
  "Saves all buffers before running cider tests"
  (interactive)

  (let* ((buffers (buffer-list))
         (test-buffers (seq-filter (lambda (b) (string-match ".*test.clj" (buffer-name b))) buffers)))
    (save-current-buffer 
      (dolist (buffer test-buffers)
        (set-buffer buffer)
        (save-buffer)
        (cider-eval-buffer)))
    (cider-test-run-project-tests nil)))

(use-package cider
  :bind
  ("C-c d" . cider-debug-defun-at-point)
  ("C-c i" . cider-inspect)
  ("C-c t" . cider-test-run-ns-tests)
  ("C-c p" . cider-profile-toggle)
  ("C-c P" . cider-profile-summary)
  ("C-c r" . cider-toggle-trace-var)
  ("C-c g" . cider-goto-test)
  ("C-c m" . cider-macroexpand-1)
  ("C-c ." . xref-find-definitions)
  ("C-c h" . cider-doc)
  :hook
  (cider-repl-mode-hook . (lambda () (setq scroll-conservatively 101)))
  :config
  (setq cider-repl-display-help-banner nil
        cider-repl-pop-to-buffer-on-connect nil
        cider-clojure-cli-aliases ":dev"
        cider-clojure-cli-command "clojure")
  (cider-auto-test-mode 1))

(defun clj-clerk-show ()
  (interactive)
  (when-let
      ((filename
        (buffer-file-name)))
    (save-buffer)
    (cider-interactive-eval
     (concat "(nextjournal.clerk/show! \"" filename "\")"))))

(defun clj-clerk-open ()
  (interactive)
  (cider-interactive-eval "(require '[nextjournal.clerk :as clerk])")
  (cider-interactive-eval "(clerk/serve! {:browse? true})")
;  (cider-interactive-eval "(clerk/serve! {:watch-paths [\"notebooks\" \"src\"]})")
  )

(defun clj-portal-open ()
  (interactive)
  (cider-interactive-eval "(require '[portal.api :as p])")
  (cider-interactive-eval "(p/open {:theme :portal.colors/zerodark})")
  (cider-interactive-eval "(add-tap #'p/submit)"))

2.8.7. clojure

(use-package clojure-mode
  :defer t
  :config
  (defface font-lock-func-face 
    '((nil (:foreground "#7F0055" :weight bold))
      (t (:bold t :italic t)))
    "Font Lock mode face used for function calls."
    :group 'font-lock-highlighting-faces)

  (font-lock-add-keywords 
   'clojure-mode
   '(("(\\s-*\\(\\_<\\(?:\\sw\\|\\s_\\)+\\)\\_>"
      1 'font-lock-func-face))))

2.8.8. company

Docs

Text completion framework.

(use-package company
  :hook
  (c++-mode . company-mode)
  (c-mode . company-mode)

  :bind(:map company-active-map
             ("<return>" . nil)
             ("RET" . nil)
             ("<tab>" . company-complete-selection)))

2.8.9. csv-mode

(use-package csv-mode)

2.8.10. dired

Docs

Builtin filesystem navigation.

(use-package dired
  :straight (:type built-in)
  :hook ((dired-mode . hl-line-mode)
         (dired-mode . dired-hide-details-mode))
  :custom
  (dired-recursive-copies 'always)
  (dired-recursive-deletes 'always)
  (dired-dwim-target t) ;;use to copy to the next buffer visible
  ;; Auto refresh Dired, but be quiet about it
  (global-auto-revert-non-file-buffers t)
  (auto-revert-verbose nil)
  :init
  (setq dired-kill-when-opening-new-dired-buffer t))

2.8.11. disaster

Github

Live disassembler for c and c++.

(use-package disaster
  :commands (disaster)
  :init
  (setq disaster-assembly-mode 'nasm-mode
          disaster-objdump "objdump -Sl --no-show-raw-insn -d")
  :bind
  ("C-c o" . disaster))

2.8.12. dockerfile

(use-package dockerfile-mode
  :defer t)

2.8.13. eglot

Github

Language server protocol (LSP) for emacs.

(use-package eglot
  :hook
  (c++-mode . eglot-ensure)
  (c-mode . eglot-ensure)
  :init
  (add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1))) )

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               `(c++-mode . ("clangd" "--compile-commands-dir=/tmp"))))

2.8.14. electric

Docs

Builtin mode for adding closed character when an open one is added (eg brackets or parens).

(use-package electric
  :hook
  (java-mode . electric-pair-mode)
  (c-mode . electric-pair-mode)
  (c++-mode . electric-pair-mode))

2.8.15. erc

Docs

Builtin irc client.

(use-package erc
  :defer t
  :config
  (setq erc-autojoin-channels-alist '(("libera.chat"))
    erc-hide-list '("JOIN" "PART" "QUIT")
    erc-server "irc.libera.chat"
    erc-nick (getenv "ERC_NICK")
    erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT")
    erc-track-shorten-function nil
    erc-join-buffer 'bury))

(defun erc-clear-notifications ()
  (interactive)
  (setq erc-modified-channels-alist nil)
  (erc-modified-channels-update))

2.8.16. exec-path-from-shell

Github

Sets the path variable for emac's spawned shells to be the same as user shells.

Tips on speeding it up: https://github.com/purcell/exec-path-from-shell/issues/75

There are interactive and non-interactive shells. Non-interactive is faster. Exec path from shell can start those but they need shell configs (.bashrc) to be in special file (.profile).

This mean that the package runtime depends on shell startup time. Conda can slow this down.
(when (memq window-system '(mac ns x))
  (use-package exec-path-from-shell
    :init
    (setq exec-path-from-shell-variables '("PATH" "MANPATH" "PYTHONPATH" "P_E" "P_A")
          exec-path-from-shell-arguments '("-l"))
    (exec-path-from-shell-initialize)))

2.8.17. flycheck

Docs

Syntax checker.

(use-package flycheck
  :hook
  (('c++-mode-hook . flycheck-mode))
  :config
  (setq flycheck-gcc-language-standard "c++17")
  (setq flycheck-clang-language-standard "c++17")
  (setq flycheck-gcc-include-path ())
  (set-face-attribute 'flycheck-error nil :underline '(:color "dark gray" :style wave))
  (set-face-attribute 'flycheck-warning nil :underline '(:color "dark gray" :style wave))
  (set-face-attribute 'flycheck-info nil :underline '(:color "light gray" :style wave)) )

2.8.18. htmlize

Docs

Supports exporting styled text from emacs to html.

(use-package htmlize)

2.8.19. ido

Docs

Auto complete for emacs inputs.

(defun ido-my-keys ()
  "Keybindings for navigating ido results ."
  (define-key ido-common-completion-map (kbd "C-n") 'ido-next-match)
  (define-key ido-common-completion-map (kbd "C-p") 'ido-prev-match)
  (define-key ido-common-completion-map (kbd "C-<return>") 'ido-select-text))

(use-package ido
  :bind
  ("C-<tab>" . ido-hippie-expand)
  ("C-j" . minibuffer-complete-and-exit)
  :init
  (setq ido-enable-regexp t)
  (setq ido-separator "\n")
  (setq ido-use-filename-at-point 'guess)
  (setq ido-create-new-buffer 'always)
  (setq ido-enable-flex-matching t)
  (setq ido-auto-merge-work-directories-length -1) ;; disable auto complete
  (ido-mode 1)
  (add-hook 'ido-setup-hook 'ido-my-keys)
  :config
  (ido-everywhere t))


2.8.20. iedit

Github

Edit all instances of a word in a buffer in realtime.

(use-package iedit
  :ensure t                             
  :bind ("C-\\" . iedit-mode))

2.8.21. json

(use-package json-mode)

2.8.22. magit

Docs

Version control for emacs.

(use-package magit
  :bind ("C-x g" . magit-status))

2.8.23. man

Docs

Man page viewer.

(use-package man
  :bind ("C-x m" . man)
  :config
  (custom-set-variables '(Man-notify-method 'bully)))

2.8.24. mu4e

Docs

Email client built on mu.

mu4e is installed when you install mu.
(straight-use-package
 '(mu4e :files (:defaults "mu4e/*.el")))

(require 'smtpmail)

(let ((addr (rot13 (base64-decode-string (getenv "P_A"))))
      (pass (rot13 (base64-decode-string (getenv "P_E"))))
      (name  "Zach Dingels")
      (mail-dir "~/docs/mail/"))
  (setq user-mail-address addr
        mu4e-get-mail-command "offlineimap"
        mu4e-mu-binary (executable-find "mu")
        mu4e-maildir mail-dir
        message-send-mail-function 'smtpmail-send-it
        smtpmail-stream-type 'tls
        smtpmail-default-smtp-server "smtp.fastmail.com"
        smtpmail-auth-credentials `(("smtp.fastmail.com" 465 addr pass))
        smtpmail-smtp-server "smtp.fastmail.com"
        smtpmail-smtp-service 465
        mu4e-sent-folder   "/sent"
        mu4e-drafts-folder "/drafts"
        mu4e-trash-folder  "/trash"
        mu4e-refile-folder "/archive"))

(setq mu4e-bookmarks
      '((:name "Inbox" :query "maildir:/Inbox" :key ?i)
        (:name "Archive" :query "maildir:/Archive" :key ?a)
        (:name "Drafts" :query "maildir:/Drafts" :key ?d)
        (:name "Sent" :query "maildir:/Sent" :key ?s)
        (:name "Note" :query "maildir:/Note" :key ?n)))

2.8.25. multiple-cursors

Github

Edit text using multiple cursors at the same time.

(use-package multiple-cursors
  :bind
  (("C->" . mc/mark-next-like-this)
   ("C-<" . mc/mark-previous-like-this)))

2.8.26. plantuml

Docs

Text based language for creating diagrams. It's compatible with org mode.

(use-package plantuml-mode
  :config
  (setq org-plantuml-jar-path (expand-file-name "~/tools/plantuml.jar"))
  (setq plantuml-default-exec-mode 'jar)
  (org-babel-do-load-languages 'org-babel-load-languages '((plantuml . t))))

2.8.27. paredit

Docs Cheatsheet

Editing and navigation for s-expressions.

(use-package paredit
  :ensure t
  :hook
  (emacs-lisp-mode-hook . enable-paredit-mode)
  (elisp-mode-hook . enable-paredit-mode)
  (eval-expression-minibuffer-setup-hook . enable-paredit-mode)
  (clojure-mode-hook . enable-paredit-mode)
  (lisp-mode-hook . enable-paredit-mode)  
  (cider-mode-hook . enable-paredit-mode)
  (janet-mode-hook . enable-paredit-mode)
  :bind
  (("M-F"     . paredit-forward-slurp-sexp)
   ("M-P"     . paredit-backward-slurp-sexp)))

2.8.28. python

(use-package python
  :defer t
  :bind
  (("C-c ["     . python-indent-shift-left)
   ("C-c ]"     . python-indent-shift-right)
   ("C-c /"     . comment-line)
   ("C-j"       . python-eval-print-last-sexp)
   ("C-c <tab>" . #'company-complete))
  :init
  (setq python-indent-guess-indent-offset t  
        python-indent-guess-indent-offset-verbose nil
        eldoc-echo-area-use-multiline-p nil
        python-shell-interpreter "/Users/zach/tools/conda/envs/dev/bin/python"))

2.8.29. rainbow deleminter

Github

Changes the color of parenthesis based on the level of nesting.

(use-package rainbow-delimiters
  :custom-face
   (rainbow-delimiters-depth-1-face ((t (:foreground "conflower blue"))))
   (rainbow-delimiters-depth-2-face ((t (:foreground "light slate blue"))))
   (rainbow-delimiters-depth-3-face ((t (:foreground "medium slate blue"))))
   (rainbow-delimiters-depth-4-face ((t (:foreground "slate blue"))))
   (rainbow-delimiters-depth-5-face ((t (:foreground "dark slate blue"))))
   (rainbow-delimiters-depth-6-face ((t (:foreground "midnight blue")))))

2.8.30. paren

Highlights the matching parenthesis when the cursor is over it's pair.

(use-package paren
  :ensure t
  :config
  (set-face-foreground 'show-paren-match "#00f")
  (set-face-attribute 'show-paren-match nil :weight 'extra-bold)
  (show-paren-mode 1))

2.8.31. slime

Docs

IDE for common lisp.

(use-package slime
  :init
  ;; (load (expand-file-name "~/.quicklisp/slime-helper.el")) ;; slows down startup.
  :defer t
  :hook
  (slime-repl-mode-hook . enable-paredit-mode)
  :config
  (setq inferior-lisp-program "sbcl")
  (show-paren-mode 1))

2.8.32. tramp

Docs

Builtin tool for working with remote machines.

use-package does not work with tramp.
(require 'tramp)

(customize-set-variable 'tramp-default-method "ssh")
(setq recentf-keep '(file-remote-p file-readable-p))
(defun tramp-ensure-dissected-file-name (vec-or-filename)
  "Return a `tramp-file-name' structure for VEC-OR-FILENAME.
VEC-OR-FILENAME may be either a string or a `tramp-file-name'.
If it's not a Tramp filename, return nil."
  (cond
   ((tramp-file-name-p vec-or-filename) vec-or-filename)
   ((tramp-tramp-file-p vec-or-filename)
    (tramp-dissect-file-name vec-or-filename))))

2.8.33. vterm

Github

A better terminal emulator for emacs.

(use-package vterm  :ensure t)

2.8.34. vundo

Github

A visualizer for the undo tree.

(use-package vundo
  :config
  (setq vundo-glyph-alist vundo-unicode-symbols))

2.8.35. yasnippet

Github

A template library.

(use-package yasnippet
  :ensure t
  :hook ((text-mode
          prog-mode
          conf-mode
          org-mode
          snippet-mode) . yas-minor-mode-on)
  :init
  (setq yas-snippet-dir "~/.emacs.d/snippets")
  :config
  (yas-global-mode 1)
  (yas-reload-all))

2.8.36. yaml

(use-package yaml-mode)

2.8.37. elisp

Various elisp libraries.

(use-package dash)
(use-package request)
(use-package s)

2.8.38. cleanup

Hide an extra buffer created for straight after all packages have been setup.

(kill-buffer "*straight-process*")

2.9. org

Official Docs

Inspirational write up on organizing org mode.

There is a lot to org mode so this section will get it's own writeup in the future…

(use-package org
  :bind
  (("C-c a" . org-agenda)
   ("C-c c" . org-capture)
   :map org-mode-map
   ("C-c b" . org-babel-tangle-block))
  :hook
  (org-mode . visual-line-mode)
  (org-agenda-finalize-hook . (lambda () (hl-line-mode)))
  (org-mode . variable-pitch-mode)
  (org-mode . buffer-face-mode)
  (org-mode . (lambda () (print "hello") (setq line-spacing 0.10)))
  :config
  ;; General
  (setq org-directory "~/org"
    org-startup-folded t
    org-hide-emphasis-markers t ;; When you use *bold*, don't display the *
    org-tags-column 1 ;
    org-html-htmlize-output-type 'css
    org-startup-with-inline-images t
    org-image-actual-width nil
    org-html-postamble t
    org-html-postamble-format '(("en" "<p class=\"date\">Date: %d</p><p class=\"author\">Author: <a href=http://zach.dingels.io>%a</a></p><p><a href=https://creativecommons.org/licenses/by-sa/4.0/>license</a></p"))
    org-html-preamble t
    org-html-preamble-format '(("en" "<p><a href=\"https://dingels.io\">home</a></p>")))
  
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Todos
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  <<org-todo>>
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Agenda
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  <<org-agenda>>
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Capture
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  <<org-capture>>
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Refile
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  <<org-refile>>
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Babel
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  <<org-babel>>
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Crypt
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  <<org-crypt>>
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  ;; Faces
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  <<org-faces>>
  )

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Customized export to html
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(with-eval-after-load 'org-html
<<org-htlm-override>>
<<org-html-others>>
<<org-html-toc-collapse>>)      

2.9.1. find-entry

Github

find-entry let's you navigate an org file like you would navigate your filesystem using IDO. It starts by giving you all the options for top level headings. After selecting a heading with tab the options will update to all the sub-headings of the selected heading.

(straight-use-package
 '(find-entry :type git :host github :repo "dingelsz/find-entry"))

(use-package find-entry
  :bind ("C-x f" . find-entry))

2.10. splash Screen

Custom text for the scratch buffer.

(setq inhibit-splash-screen t)
(switch-to-buffer "*scratch*")
(insert (concat 
         ";; ----------------------------------------------------------------------------\n"
         ";; Festina Lente\n"
         ";; ----------------------------------------------------------------------------\n"))
(end-of-buffer)
(load custom-file)

3. email

mu4e is an email client for emacs. It uses mu to index and search email. It requires a third tool to retrieve email. I use offlineimap with the configs below.

3.1. install

sudo port install mu
sudo port install offlineimap

3.2. configs

# ~/.config/offlineimap/config
  
[general]
# List of accounts to be synced, separated by a comma.
accounts = main
pythonfile = ~/.offlineimap.py

[Account main]
# Identifier for the local repository; e.g. the maildir to be synced via IMAP.
localrepository = local
# Identifier for the remote repository; i.e. the actual IMAP, usually non-local.
remoterepository = fastmail

[Repository local]
# OfflineIMAP supports Maildir, GmailMaildir, and IMAP for local repositories.
type = Maildir
# Where should the mail be placed?
localfolders = ~/docs/mail

[Repository fastmail]
# Remote repos can be IMAP or Gmail, the latter being a preconfigured IMAP.
# SSL and STARTTLS are enabled by default.
type = IMAP
remotehost = imap.fastmail.com
remoteusereval = getuser()
remotepasseval = getpass()
ssl = yes
# Necessary for SSL connections, if using offlineimap version > 6.5.4
sslcacertfile = ~/docs/mail/.cert.pem
# ~/.offlineimap.py

import base64
import codecs
import os

def decode(x): return codecs.decode(base64.b64decode(x).decode(), "rot_13")
def getuser(): return decode(os.environ["P_A"])
def getpass(): return decode(os.environ["P_E"])

3.3. useful links

4. sync via git

See org->git for an overview of how to automatically sync your org files with git.

4.1. cron

# M H D M W /PATH/TO/SCRIPT
  * * * * * /usr/local/sbin/org-sync >> /Users/zach/org/org-sync.log 2>&1

4.2. systemd

Date: 02.10.24

Author: Zach Dingels

license