Preamble

My emacs configuration. It tangles to .emacs.d/init.el and exports to this markdown file on save. If you’re viewing this in a repo, it’s better to visit the website at https://iankeane.srht.site/dotfiles. If you are viewing this on the website and want the repo, you can find it at https://sr.ht/~iankeane/dotfiles. To see the hugo front-matter configuration, you’ll have to view the raw code.

Table of Contents

  1. Preamble
  2. First things first
    1. Literate Config Setup
    2. Package initialization
    3. Environment-specific setup
    4. Secrets
    5. Global variables
    6. Handle config file
    7. Required directories for emacs setup
    8. Helper Functions
  3. One-Line Includes
  4. Notes
  5. AI
  6. Org Mode
  7. Basic Appearance and Behaviors
    1. Tab bar
  8. Navigation
    1. Helper Functions
    2. Direct keymaps
    3. Windows and Workspaces
    4. Dired
    5. Treemacs
    6. Which-key
    7. General
  9. Project management
  10. Autocomplete
    1. Vertico
    2. Consult
    3. Generic completion
    4. Orderless
    5. Marginalia
    6. Company-mode
    7. Embark
    8. Snippets
  11. Languages
    1. LSP
    2. Rust
    3. Terraform
    4. Lisp
  12. Markups
    1. Markdown
    2. Beancount
  13. Shells
    1. Enable fish completions
  14. Git
  15. Meow
  16. Evil
  17. Web browsing and youtube
  18. Todo
    1. General
    2. Terminals
    3. New functions
    4. Note taking
    5. One-offs
    6. LSP stuff
    7. Completion
    8. Keybindings
    9. New plugins
    10. Appearance switches
    11. Research
    12. System crafters videos
    13. Clean up Org Roam
    14. Snips from old config

First things first

Literate Config Setup

To get started, run C-c C-v t

(defun dotfiles/org-babel-tangle-config ()
(when (string-equal (buffer-file-name)
            (expand-file-name "~/dotfiles/guix/init.org"))
  (let ((org-confirm-babel-evaluate nil))
    (org-babel-tangle))))

(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'dotfiles/org-babel-tangle-config)))

Package initialization

Use-package

  ;; Initialize Package Sources
  (require 'package)

  (setq package-archives
    '(("melpa" . "https://melpa.org/packages/")
      ("org" . "https://orgmode.org/elpa/")
      ("elpa" . "https://elpa.gnu.org/packages/")))
  (package-initialize)

  ;; Initialize use-package on non-linux platforms
  (unless (package-installed-p 'use-package)
(package-install 'use-package))

  (require 'use-package)
  (setq use-package-always-ensure t)

  (package-refresh-contents)
  ;; Persist history over Emacs restarts. Vertico sorts by history position.
  (use-package savehist
:init
(savehist-mode))

Straight

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        (or (bound-and-true-p straight-base-dir)
            user-emacs-directory)))
      (bootstrap-version 7))
  (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))

(setq package-enable-at-startup nil)

Environment-specific setup

(setq IS-MACOS (eq system-type 'darwin))

(setq openai-key-string
 (if IS-MACOS "self/openai-alt" "openai-alt"))

Secrets

;; (use-package pinentry
;;   :ensure t)
;; (use-package pass
;;   :ensure t)
;; (use-package password-store)
;; (pinentry-start)
;; (setq gptel-api-key (password-store-get openai-key-string))  ;; TODO if-macos

Global variables

Handle config file

(setq user-init-file (expand-file-name "~/.config/emacs/init.el"))

(defun reload-config ()
"Reload the Emacs configuration."
(interactive)
(load-file user-init-file))

Required directories for emacs setup

(defun ensure-directory (dir)
  "Ceate a dir if it doesn't exist."
  (unless (file-directory-p dir)
    (make-directory dir t)))

(ensure-directory "~/.config/emacs/backups/")
(ensure-directory "~/.config/emacs/autosaves")
(ensure-directory "~/notes/roam")
(ensure-directory "~/notes/deft")
(ensure-directory "~/notes/code")
(ensure-directory "~/.config/emacs/video")
(ensure-directory "~/.config/emacs/audio")

(setq projectile-project-search-path '("~/code/")
      empv-video-dir "~/.config/emacs/video"
      empv-audio-dir "~/.config/emacs/audio"
      backup-directory-alist `(("." . "~/.config/emacs/backups/"))
      auto-save-file-name-transforms `((".*" "~/.config/emacs/autosaves/" t)))

Helper Functions


(defun ian/eval-region-or-buffer ()
  "Eval the region if anything is selected, otherwise eval the whole buffer."
  (interactive)
  (if mark-active
      (eval-region (region-beginning) (region-end))
    (eval-buffer)))

(defun ian/conditional-imenu ()
"Call `consult-org-heading` in Org mode, otherwise `consult-imenu`."
(interactive)
(if (eq major-mode 'org-mode)
    (consult-org-heading)
  (consult-imenu)))

One-Line Includes

(use-package treemacs)
;; Enable follow mode
;; Use treemacs-current-visibility function to expand backspace functionality
(setq treemacs--project-follow-delay 0)
(defun ian/treemacs-open-current-or-jump ()
"Find the current file and open treemacs if it's not open, otherwise jump
to the treemacs window"
(interactive)
    (if
        (not(eq (treemacs-current-visibility) 'visible))
        (treemacs-find-file))
  (treemacs-select-window))

Notes

(use-package deft)
(setq deft-recursive t
      deft-extensions '("org" "txt"))

AI

(use-package gptel)
(setq gptl-default-mode 'org-mode)

Org Mode

(use-package org)
;; (add-hook 'org-mode-hook 'yas-minor-mode)
(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (python . t)))

(setq org-confirm-babel-evaluate nil)

(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))

(add-to-list 'org-structure-template-alist '("py" . "src python"))

(setq org-hide-emphasis-markers t)

(use-package org-appear
  :hook (org-mode . org-appear-mode))

(use-package org-roam
  :ensure t
  :custom
  (org-roam-directory "~/notes/roam")
  :config
  ;; If you're using a vertical completion framework, you might want a more informative completion interface
  ;; (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
  (org-roam-db-autosync-mode))

(use-package deft
  :commands (deft)
  :config
  (setq deft-directory "~/notes/deft"
        deft-extensions '("org" "txt")
        deft-recursive t))

(use-package ox-hugo
  :after ox)

(use-package org-roam-ui
  :after org-roam
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t))

(use-package ox-gfm)

Basic Appearance and Behaviors

(setq inhibit-startup-message t)
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(recentf-mode 1)
(savehist-mode 1)  ; remember previous commands in minibuffer
(save-place-mode 1)  ; put cursor in last place when opening a file

;; Avoid customization UI auto-variables in init.el
(setq custom-file (locate-user-emacs-file "custom-vars.el"))
(load custom-file 'noerror 'nomessage)

;; Don't show popup UI when prompting
(setq use-dialog-box nil)

;; Update files when changed on disk
(global-auto-revert-mode 1)

(use-package solaire-mode
  :config (solaire-global-mode +1))

(use-package doom-themes
:ensure t
:config
;; Global settings (defaults)
(setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
      doom-themes-enable-italic t) ; if nil, italics is universally disabled
;; (load-theme 'doom-one t)

;; Enable flashing mode-line on errors
(setq doom-themes-treemacs-theme "doom-atom") ; use "doom-colors" for less minimal icon theme
(doom-themes-treemacs-config)
;; Corrects (and improves) org-mode's native fontification.
(doom-themes-org-config))

(set-frame-font "DejaVu Sans Mono 17")
(use-package simple-modeline
  :hook (after-init . simple-modeline-mode))

(use-package base16-theme)
(load-theme 'base16-oceanicnext t)

(global-display-line-numbers-mode t)
(dolist (mode
         '(org-mode-hook
           term-mode-hook
           eshell-mode-hook))
  (add-hook mode (lambda () (display-line-numbers-mode 0))))
(column-number-mode)


(use-package default-text-scale
  :defer 1
  :config
  (default-text-scale-mode))


(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode))

(use-package helpful)  ; Slightly more helpful help
(setq tab-always-indent 'complete)
(setq tab-first-completion 'word)

;; Generate buffers in last window
(setq display-buffer-base-action
      '(display-buffer-reuse-mode-window
        display-buffer-reuse-window
        display-buffer-same-window))

;; If a popup does happen, don't resize windows to be equal-sized
(setq even-window-sizes nil)
(setq whitespace-style '(trailing tabs newline tab-mark )) ; show tab characters
(setq indent-tabs-mode nil)  ; tabs not spaces

Tab bar

(use-package tab-bar-echo-area
  :ensure
  :config
  (tab-bar-echo-area-mode 1)
  (setq tab-bar-show nil))

Navigation

Helper Functions

(defun split-and-follow-horizontally ()
  (interactive)
  (split-window-below)
  (balance-windows)
  (other-window 1))

(defun split-and-follow-vertically ()
  (interactive)
  (split-window-right)
  (balance-windows)
  (other-window 1))

Direct keymaps

;; (global-set-key (kbd "s-c") 'delete-window)
;; (global-set-key (kbd "s-h") 'windmove-left)
;; (global-set-key (kbd "s-j") 'windmove-down)
;; (global-set-key (kbd "s-k") 'windmove-up)
;; (global-set-key (kbd "s-l") 'windmove-right)
;; (define-key evil-normal-state-map (kbd "s-d") 'kill-this-buffer)
;; (define-key evil-normal-state-map (kbd "s-n") 'projectile-next-project-buffer)
;; (define-key evil-normal-state-map (kbd "s-p") 'projectile-previous-project-buffer)
;; (define-key evil-normal-state-map (kbd "s-J") 'split-and-follow-horizontally)
;; (define-key evil-normal-state-map (kbd "s-L") 'split-and-follow-vertically)
;; (define-key evil-normal-state-map (kbd "s-b") 'buffer-menu)
;; (define-key evil-normal-state-map (kbd "s-x") 'execute-extended-command)
;; (global-set-key (kbd "s-SPC") 'tab-next)
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)

(use-package winner
  :after evil
  :config
  (winner-mode))

Windows and Workspaces

Ace-window

Credit for manual dispatch function

(defun ace-window-manual-dispatch (arg)
  "Calls `ace-window' with ARG asking for the dispatch, even if `aw-dispatch-always' is nil.
  This could be useful to use the advanced commands"
  (interactive "p")
  (let ((aw-dispatch-always t))
    (ace-window arg)
    ))
(use-package ace-window)
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
(setq aw-dispatch-alist
      '(
        (?x aw-delete-window "Delete Window")
        (?m aw-swap-window "Swap Windows")
        (?M aw-move-window "Move Window")
        ;; (?c aw-copy-window "Copy Window")
        ;; (?j aw-switch-buffer-in-window "Select Buffer")
        ;; (?n aw-flip-window)
        ;; ;;
        (?b aw-switch-buffer-other-window "Switch Buffer Other Window")
        (?u winner-undo)
        (?U winner-redo)
        (?c aw-split-window-fair "Split Fair Window")
        (?v aw-split-window-vert "Split Vert Window")
        (?H aw-split-window-horz "Split Horz Window")
        (?o delete-other-windows "Delete Other Windows")
        (?? aw-show-dispatch-help)))
;; TODO: Window width controls

Dired

(setq ring-bell-function 'ignore)
(setq dired-listing-switches "-al")


(setq dired-dwim-target t) ; opening 'other window' on a subdir enters 2 window dired mode for copy/paste
(use-package dired-single)

(defun my-dired-init ()
  "Bunch of stuff to run for dired, either immediately or when it's
 loaded."
  ;; <add other stuff here>
  (define-key dired-mode-map [remap dired-find-file]
              'dired-single-buffer)
  (define-key dired-mode-map [remap dired-mouse-find-file-other-window]
              'dired-single-buffer-mouse)
  (define-key dired-mode-map [remap dired-up-directory]
              'dired-single-up-directory))

;; if dired's already loaded, then the keymap will be bound
(if (boundp 'dired-mode-map)

    ;; we're good to go; just add our bindings
    (my-dired-init)
  ;; it's not loaded yet, so add our bindings to the load-hook
  (add-hook 'dired-load-hook 'my-dired-init))

(use-package dired-open
  :config
  (setq dired-open-extensions '(("png" . "sxiv")
                                ("mkv" . "mpv")
                                ("avi" . "mpv"))))

(use-package all-the-icons-dired

  :hook (dired-mode . all-the-icons-dired-mode))

Treemacs

(use-package all-the-icons)
(use-package treemacs-all-the-icons)

Which-key

(use-package which-key
  :init (which-key-mode)
  :diminish which-key-mode
  :config
  (setq which-key-idle-delay 0.3))

General

  • map spc W for split and winner-undo / redo operations on 2 windows
  • bind persp-mode
  • bind lispy for colemak-dh
  • bind lispy doc features and ide-like features
  • bind embark
  • bind commentary
  • bind treemacs-select-window
(use-package general
  :ensure t
  :config

  (general-create-definer general-definition
    :keymaps '(normal insert visual emacs)
    :prefix "SPC"
    :global-prefix "C-SPC")

  (general-definition

    ;; Top level bindings
    "SPC" '(projectile-find-file                      :which-key "Find project file")
    "<backspace>" '(ian/treemacs-open-current-or-jump :which-key "Find current file in sidebar") ; TODO or treemacs-select-window
    "," '(+ivy/switch-workspace-buffer                :which-key "Buffers (workspace)")
    "/" '(consult-ripgrep                             :which-key "Search project")
    ":" '(execute-extended-command                    :which-key "M-x")
    ";" '(eval-expression                             :which-key "Eval")
    "<" '(consult-buffer                              :which-key "Buffers (all)")
    "x" '(scratch-buffer                              :which-key "Scratch popup")
    "-" '(dired                                       :which-key "Find Directory")
    ;; "*" '(counsel-locate                           :which-key "Fuzzy find file") ;; find-file?  consult-locate?
    "d" '(docker                                      :which-key "Docker")
    "i" '(ian/conditional-imenu                       :which-key "Imenu")  ;; consult-outline?
    "w" '(ace-window                                  :which-key "Window")
    "W" '(ace-window-manual-dispatch                  :which-key "Window (dispatch)")

    ;; Agenda
    "a" '(:ignore t                     :which-key "Agenda")
    "aa" '(org-agenda                   :which-key "Open Agenda")
    "a/" '(consult-org-agenda           :which-key "Search agenda headlines")

    "ac" '(:ignore t                    :which-key "Clock")
    "acc" '(org-clock-goto              :which-key "Active clock")
    "ack" '(org-clock-cancel            :which-key "Cancel current clock")
    "act" '(+org/toggle-last-clock      :which-key "Toggle last clock")


    ;; Buffers / bookmarks
    "b" '(:ignore t                 :which-key "Buffers")
    "bb" '(consult-bookmark         :which-key "Goto Bookmark")  ;; consult-bookmark
    "bk" '(kill-current-buffer      :which-key "Kill buffer")
    "bm" '(bookmark-set             :which-key "Set bookmark")
    "bM" '(bookmark-delete          :which-key "Delete bookmark")
    ;; "bo" '(NEEDS CUSTOM FUNCTION :which-key "Kill other buffers")
    "br" '(revert-buffer            :which-key "Revert buffer")
    "bR" '(rename-buffer            :which-key "Rename buffer")
    "bz" '(bury-buffer              :which-key "Bury buffer")
    ;; "bZ" '(NEEDS CUSTOM FUNCTION :which-key "Kill buried buffers")
    "bj" '(+ivy/jump-list           :which-key "Jump list")
    "bu" '(vundo                    :which-key "Undo history")

    ;; Code
    "c" '(:ignore t                         :which-key "Code")
    "cc" '(compile                          :which-key "Compile")
    "cC" '(recompile                        :which-key "Recompile")
    "cd" '(+lookup/definition               :which-key "Goto definition")
    "cr" '(+lookup/references               :which-key "Goto references")
    "ce" '(ian/eval-region-or-buffer     :which-key "Eval buffer/region")
    ;; "cf" '(NOT A COMMAND (re-do)         :which-key "Format buffer/region")
    "ci" '(+lookup/implementations          :which-key "Find implementations")
    "cj" '(lsp-ivy-workspace-symbol         :which-key "Goto symbol (curr. workspace)")
    "cJ" '(lsp-ivy-global-workspace-symbol  :which-key "Goto symbol (any workspace)")
    "ck" '(+lookup/documentation            :which-key "Jump to documentation")
    "cl" '(+default/lsp-command-map         :which-key "LSP")
    "co" '(lsp-organize-imports             :which-key "LSP organize imports")
    "cR" '(lsp-rename                       :which-key "LSP rename")
    "cs" '(+eval/send-region-to-repl        :which-key "Send to repl")
    "ct" '(+lookup/type-definition          :which-key "Find type definition")
    "cw" '(delete-trailing-whitespace       :which-key "Delete trailing whitespace")
    ;; "cn" '(doom/delete-trailing-newlines :which-key "Delete trailing newlines")
    "cx" '(+default/diagnostics             :which-key "List errors")

    ;; File
    "f" '(:ignore t                                      :which-key "File")
    ;; "fp" '(doom/open-private-config                   :which-key "Browse private config")
    "fr" '(consult-recent-file                           :which-key "Recent files")
    "fR" '(reload-config                                 :which-key "Reload config")
    ;; "fm" '(doom/move-this-file                        :which-key "Move/rename file")
    ;; "fS" '(doom/sudo-find-file                        :which-key "Sudo find file")
    ;; "fs" '(doom/sudo-this-file                        :which-key "Sudo this file")
    "fy" '(+default/yank-buffer-path                     :which-key "yank file path")
    "fY" '(+default/yank-buffer-path-relative-to-project :which-key "Yank file path from project")

    ;; Git
    "g"   '(:ignore t :which-key "git")
    "gb"  '(magit-branch-checkout         :which-key "magit switch branch")
    "gB"  '(magit-blame-addition          :which-key "magit blame")
    "gF"  '(magit-fetch                   :which-key "magit fetch")
    "gg"  '(magit-status                  :which-key "magit status")
    "gG"  '(magit-status-here             :which-key "magit status here")
    "gL"  '(magit-log-buffer-file         :which-key "magit buffer log")
    "gt"  '(git-timemachine-toggle        :which-key "git time machine")

    "gd"   '(:ignore t :which-key "dispatch")
    "gdF" '(forge-dispatch                :which-key "Forge dispatch")
    "gdd" '(magit-dispatch                :which-key "Magit dispatch")
    "gdf" '(magit-file-dispatch           :which-key "Magit file dispatch")

    "gf"   '(:ignore t :which-key "find")
    "gfc" '(magit-show-commit             :which-key "find commit")
    "gff" '(magit-find-file               :which-key "find file")
    "gfg" '(magit-find-git-config-file    :which-key "find gitconfig file")
    "gfi" '(forge-visit-issue             :which-key "find issue")
    "gfp" '(forge-visit-pullreq           :which-key "find pull request")

    "gh"   '(:ignore t :which-key "hunk")

    "ghn" '(+vc-gutter/previous-hunk      :which-key "jump to previous hunk")
    "ghp" '(+vc-gutter/next-hunk          :which-key "jump to next hunk")
    "ghr" '(+vc-gutter/revert-hunk        :which-key "revert hunk at point")
    "ghs" '(+vc-gutter/stage-hunk         :which-key "stage hunk at point")

    "gl"   '(:ignore t :which-key "list")
    "gli" '(forge-list-issues             :which-key "list issues")
    "gln" '(forge-list-notifications      :which-key "list notifications")
    "glp" '(forge-list-pullreqs           :which-key "list pull requests")
    "glr" '(forge-list-issues             :which-key "list repositories")
    "gls" '(magit-list-submodules         :which-key "list submodules")

    "go"   '(:ignore t :which-key "open")
    "goI" '(forge-browse-issues           :which-key "Browse issues")
    "goP" '(forge-browse-pullreqs         :which-key "Browse pull requests")
    "goc" '(forge-browse-commit           :which-key "Browse commit")
    "goh" '(+vc/browse-at-remote-homepage :which-key "Browse homepage")
    "goi" '(forge-browse-issue            :which-key "Browse an issue")
    "goo" '(+vc/browse-at-remote          :which-key "Browse file or region")
    "gop" '(forge-browse-pullreq          :which-key "Browse a pull request")
    "gor" '(forge-browse-remote           :which-key "Browse remote")

    ;; Help
    "h"   '(:ignore t :which-key "help")
    "h<return>" '(info-emacs-manual                :which-key "Emacs manual")
    "h'"        '(describe-char                    :which-key "Describe character")
    "ha"        '(apropos                          :which-key "Apropos")
    "hb"        '(embark-bindings                  :which-key "Describe bindings")
    "he"        '(view-echo-area-messages          :which-key "View echo area messages")
    "hf"        '(helpful-function                 :which-key "Describe function")
    "hi"        '(info                             :which-key "Info")
    "hk"        '(helpful-key                      :which-key "Describe key")
    "hl"        '(view-lossage                     :which-key "View actions log")
    "hm"        '(describe-mode                    :which-key "Describe mode")
    "ho"        '(helpful-symbol                   :which-key "Describe symbol")
    "hv"        '(helpful-variable                 :which-key "Describe variable")
    "hx"        '(helpful-command                  :which-key "Describe command")
    "hA"        '(apropos-documentation            :which-key "Apropos(documentation)")
    "hF"        '(describe-face                    :which-key "Describe Face")
    "hM"        '(popwin:messages                  :which-key "View system messages")

    "hw"        '(:ignore t :which-key "which-key")
    "hwf"       '(which-key-show-full-keymap       :which-key "Full keymap")
    "hwi"       '(which-key-show-minor-mode-keymap :which-key "Minor mode keymap")
    "hwk"       '(which-key-show-keymap            :which-key "Show keymap")
    "hwm"       '(which-key-show-major-mode        :which-key "Show major mode")
    "hwt"       '(which-key-show-top-level         :which-key "Show top-level")

    "m"       '(:ignore t :which-key "media")
    "my"      '(empv-youtube          :which-key "youtube")
    "mt"      '(empv-toggle           :which-key "toggle")
    "mT"      '(empv-toggle-video     :which-key "toggle-video")
    "mq"      '(empv-exit             :which-key "quit")
    "ms"      '(empv-display-current  :which-key "show currently playing")

    "mr"      '(:ignore t :which-key "radio")
    "mrr"     '(empv-play-radio                    :which-key "play")
    "mrR"     '(empv-play-random-channel           :which-key "play random")
    "mrl"     '(empv-log-current-radio-song-name   :which-key "log song name")

    "mp"      '(:ignore t :which-key "playlist")
    "mpP"     '(empv-playlist                 :which-key "playlist")
    "mps"     '(empv-playlist-select  :which-key "select")
    "mpn"     '(empv-playlist-next    :which-key "next")
    "mpp"     '(empv-playlist-prev    :which-key "prev")
    "mpc"     '(empv-playlist-clear   :which-key "clear")
    "mpS"     '(empv-playlist-shuffle :which-key "shuffle")

    "mf"      '(:ignore t :which-key "file")
    "mff"     '(empv-play-file        :which-key "play file")
    "mfd"     '(empv-play-directory   :which-key "play directory")

    "mv"      '(:ignore t :which-key "volume")
    "mvd"     '(empv-volume-down      :which-key "down")
    "mvu"     '(empv-volume-up        :which-key "up")

    "mc"      '(:ignore t :which-key "chapter")
    "mcp"     '(empv-chapter-prev     :which-key "prev")
    "mcn"     '(empv-chapter-next     :which-key "next")
    "mcs"     '(empv-chapter-select   :which-key "select")

    ;; Notes
    "nd" '(deft                              :which-key "Deft")
    "nt" '(org-todo-list                     :which-key "Todo list")
    "nf" '(org-roam-node-find                :which-key "Find node")
    "ng" '(org-roam-ui-open                  :which-key "Show graph")
    "nr" '(org-roam-buffer-toggle            :which-key "Toggle roam buffer")
    "nb" '(org-roam-buffer-display-dedicated :which-key "Launch roam buffer")
    "ns" '(org-roam-db-sync                  :which-key "Sync database")
    "nc" '(org-roam-capture                  :which-key "Capture to node")

    ;; Org mode
    "o" '(:ignore t :which-key "org")
    "ol"  '(org-store-link                        :which-key "Org store link")
    "oc"  '(org-capture                           :which-key "Org Capture")
    "oC"  '(org-capture-goto-target               :which-key "Goto Capture")
    "ot"  '(org-babel-tangle                      :which-key "Org Tangle")
    "oy"  '(+org/export-to-clipboard              :which-key "Org export to clipboard")
    "oY"  '(+org/export-to-clipboard-as-rich-text :which-key "Org export to clipboard (rich)")
    "oe" '(:ignore t :which-key "export")
    "oeh" '(org-html-export-to-html               :which-key "Export Org to HTML")
    "oa" '(:ignore t :which-key "attach")
    "oaa" '(org-attach                            :which-key "Attach")
    "oac" '(org-download-clipboard                :which-key "Org download clipboard")
    "oad" '(org-attach-delete-one                 :which-key "Delete one attachment")
    "oaD" '(org-attach-delete-all                 :which-key "Delete all attachments")
    "oas" '(org-attach-sync                       :which-key "Sync attachments")
    "oay" '(org-download-yank                     :which-key "Org download yank")

    ;; Project
    "p"  '(:ignore t :which-key "project")
    "ps" '(+neotree/open                     :which-key "Open project sidebar")
    "pa" '(projectile-add-known-project      :which-key "Add new project")
    "pc" '(+ivy/project-compile              :which-key "Compile in project")
    "pd" '(projectile-remove-known-project   :which-key "Remove known project")
    "ps" '(projectile-switch-project         :which-key "Switch projects")
    "pC" '(projectile-configure-project      :which-key "Configure project")
    "pr" '(projectile-run-project            :which-key "Run project")
    "pt" '(magit-todos-list                  :which-key "List project todos")
    "pT" '(projectile-test-project           :which-key "Test project")
    "pz" '(+taskrunner/project-tasks         :which-key "List  project tasks")

    ;; Shell
    "s"  '(:ignore t :which-key "shell")
    "se" '(+eshell/toggle               :which-key "Toggle eshell popup")
    "sE" '(+eshell/here                 :which-key "Open eshell here")
    "sr" '(+eval/open-repl-other-window :which-key "Repl")
    "st" '(+vterm/toggle                :which-key "Toggle vterm popup")
    "sT" '(+vterm/here                  :which-key "Open vterm here")

    ;; Toggle
    "t"  '(:ignore t :which-key "toggle")
    "tc" '(global-display-fill-column-indicator-mode :which-key "Fill Column Indicator")
    "tf" '(flycheck-mode                             :which-key "Flycheck")
    "tm" '(consult-minor-mode-menu                   :which-key "Minor modes")
    "tp" '(org-tree-slide-mode                       :which-key "Org-tree-slide-mode")
    "tw" '(+word-wrap-mode                           :which-key "Soft line wrapping")
    "tz" '(+zen/toggle                               :which-key "Zen mode")
    ;; "tl" '(doom/toggle-line-numbers                  :which-key "Line numbers")

    ;; Visual / appearance
    "v"  '(:ignore t  :which-key "visual/appearance")
    "vt" '(load-theme :which-key "load theme")

    ;; Workspaces
    "TAB TAB" '(+workspace/display              :which-key "Display tab bar")
    "TAB d"   '(+workspace/delete               :which-key "Delete this workspace")
    "TAB l"   '(+workspace/load                 :which-key "Load workspace from file")
    "TAB n"   '(+workspace/new                  :which-key "New workspace")
    "TAB N"   '(+workspace/new-named            :which-key "New named workspace")
    "TAB r"   '(+workspace/rename               :which-key "Rename workspace")
    "TAB R"   '(+workspace/restore-last-session :which-key "Restore last session")
    "TAB s"   '(+workspace/save                 :which-key "Save workspace")
    "TAB x"   '(+workspace/kill-session         :which-key "Delete session")
    ))

Project management

(use-package projectile)  ; todo config inside use-package
(use-package treemacs-projectile)

Autocomplete

Vertico

(use-package vertico
    :init
    (vertico-mode)

    ;; Different scroll margin
    ;; (setq vertico-scroll-margin 0)

    ;; Show more candidates
    ;; (setq vertico-count 20)

    ;; Grow and shrink the Vertico minibuffer
    ;; (setq vertico-resize t)

    ;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
    ;; (setq vertico-cycle t)
)

Consult

(use-package consult
  :bind (:map minibuffer-local-map ("C-r" . consult-history)))

Generic completion

Idk I think this came from the vertico readme

(use-package emacs
:init
;; Add prompt indicator to `completing-read-multiple'.
;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
(defun crm-indicator (args)
    (cons (format "[CRM%s] %s"
                (replace-regexp-in-string
                    "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                    crm-separator)
                (car args))
        (cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)

;; Do not allow the cursor in the minibuffer prompt
(setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)

;; Emacs 28: Hide commands in M-x which do not work in the current mode.
;; Vertico commands are hidden in normal buffers.
;; (setq read-extended-command-predicate
;;       #'command-completion-default-include-p)

;; Enable recursive minibuffers
(setq enable-recursive-minibuffers t))
(setq completion-styles '(basic substring partial-completion flex))

Orderless

(use-package orderless
  :init
  ;; Configure a custom style dispatcher (see the Consult wiki)
  ;; (setq orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch)
  ;;       orderless-component-separator #'orderless-escapable-split-on-space)
  (setq completion-styles '(orderless basic)
        completion-category-defaults nil
        completion-category-overrides '((file (styles partial-completion)))))

Marginalia

(use-package marginalia
  ;; Bind `marginalia-cycle' locally in the minibuffer.  To make the binding
  ;; available in the *Completions* buffer, add it to the
  ;; `completion-list-omode-map'.
  :bind (:map minibuffer-local-map
              ("M-A" . marginalia-cycle))

  ;; The :init section is always executed.
  :init

  ;; Marginalia must be activated in the :init section of use-package such that
  ;; the mode gets enabled right away. Note that this forces loading the
  ;; package.
  (marginalia-mode))

Company-mode

(use-package company
  :ensure t
  :hook (prog-mode . company-mode)
  :diminish
  :bind (:map company-active-map
              ("<tab>" . company-select-next)
              ("TAB" . company-select-next)
              ("<backtab>" . company-select-previous)
              ("<S-tab>" . company-select-previous))
  (:map lsp-mode-map  ; more general mode than this?
        ("<tab>" . company-indent-or-complete-common))
  :config
  (global-company-mode 1)
  :init
  (setq company-idle-delay nil))

Embark

  (use-package embark
  :ensure t
  :diminish eldoc-mode
  :bind
  (("C-." . embark-act)         ;; pick some comfortable binding
   ("C-;" . embark-dwim)        ;; good alternative: M-.
   ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'
  :init

  ;; Show the Embark target at point via Eldoc.  You may adjust the Eldoc
  ;; strategy, if you want to see the documentation from multiple providers.
  (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)

  :config
  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

;; Consult users will also want the embark-consult package.
(use-package embark-consult
  :ensure t ; only need to install it, embark loads it after consult if found
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

Snippets

(use-package yasnippet
  :diminish yas-minor-mode)
(use-package yasnippet-snippets
  :after lsp-mode)
(yas-global-mode)

Languages

LSP

(use-package lsp-mode
  :commands (lsp lsp-deferred)
  :init
  (setq lsp-keymap-prefix "C-c l")
  :config
  (lsp-enable-which-key-integration t)
  :hook (terraform-mode . lsp-deferred)
  :hook (rust-mode . lsp-deferred))

(use-package lsp-ui)
(use-package lsp-treemacs)
(use-package flycheck
  :ensure t
  :diminish
  :init (global-flycheck-mode))

Rust

(use-package rust-mode
  :ensure t
  :mode "\\.rs\\'"
  :config
  (setq rust-format-on-save t)) ; Automatically format rust files on save

(use-package cargo
  :ensure t
  :hook (rust-mode . cargo-minor-mode))

Terraform

(use-package terraform-mode
  :ensure t)

Lisp

All lisp-family languages

(use-package lispy)
(use-package lispyville
  :init
  (general-add-hook '(emacs-lisp-mode-hook lisp-mode-hook) #'lispyville-mode)
  :config
  (lispyville-set-key-theme '(operators c-w additional)))

Markups

Markdown

(use-package markdown-mode
  :mode ("\\.md\\'" . markdown-mode))
;;(use-package evil-markdown
;;  :mode ("\\.md\\'" . evil-markdown-mode))
(use-package json-mode)
(use-package yaml-mode)

Beancount

  • TODO: bind the beancount functions to conditional mode binding
(defun beancount-tab-function ()
  "Improved beancount tab ripped off from doom emacs"
  (interactive)
  (if (and outline-minor-mode (outline-on-heading-p))
      (beancount-outline-cycle)
    (indent-according-to-mode)))

(use-package beancount
  :straight (beancount :type git :host github :repo "beancount/beancount-mode")
  :mode ("\\.beancount\\'" . beancount-mode)
  :hook (beancount-mode . outline-minor-mode)
  :config
  (evil-define-key 'normal 'beancount-mode-map (kbd "TAB") 'beancount-tab-function)
  ) ;; Bind the TAB key

(add-hook 'beancount-mode-hook #'flymake-bean-check-enable)

Shells

Enable fish completions

Uses the external fish program to provide fish-style completions to eshell etc

(use-package fish-completion)
(when (and (executable-find "fish")
           (require 'fish-completion nil t))
  (global-fish-completion-mode))

Git

(use-package git-gutter+
  :diminish)
(global-git-gutter+-mode t)
(use-package magit)
(use-package magit-todos)
(setq magit-display-buffer-function 'magit-display-buffer-same-window-except-diff-v1)

Meow

;  (use-package 'meow)
;  (defun meow-setup ()
;      (setq meow-cheatsheet-layout meow-cheatsheet-layout-colemak)
;      (meow-motion-overwrite-define-key
;       ;; Use e to move up, n to move down.
;       ;; Since special modes usually use n to move down, we only overwrite e here.
;       '("e" . meow-prev)
;       '("<escape>" . ignore))
;      (meow-leader-define-key
;       '("?" . meow-cheatsheet)
;       ;; To execute the originally e in MOTION state, use SPC e.
;       '("e" . "H-e")
;       '("1" . meow-digit-argument)
;       '("2" . meow-digit-argument)
;       '("3" . meow-digit-argument)
;       '("4" . meow-digit-argument)
;       '("5" . meow-digit-argument)
;       '("6" . meow-digit-argument)
;       '("7" . meow-digit-argument)
;       '("8" . meow-digit-argument)
;       '("9" . meow-digit-argument)
;       '("0" . meow-digit-argument))
;      (meow-normal-define-key
;       '("0" . meow-expand-0)
;       '("1" . meow-expand-1)
;       '("2" . meow-expand-2)
;       '("3" . meow-expand-3)
;       '("4" . meow-expand-4)
;       '("5" . meow-expand-5)
;       '("6" . meow-expand-6)
;       '("7" . meow-expand-7)
;       '("8" . meow-expand-8)
;       '("9" . meow-expand-9)
;       '("-" . negative-argument)
;       '(";" . meow-reverse)
;       '("," . meow-inner-of-thing)
;       '("." . meow-bounds-of-thing)
;       '("[" . meow-beginning-of-thing)
;       '("]" . meow-end-of-thing)
;       '("/" . meow-visit)
;       '("a" . meow-append)
;       '("A" . meow-open-below)
;       '("b" . meow-back-word)
;       '("B" . meow-back-symbol)
;       '("c" . meow-change)
;       '("d" . meow-delete)
;       '("e" . meow-prev)
;       '("E" . meow-prev-expand)
;       '("f" . meow-find)
;       '("g" . meow-cancel-selection)
;       '("G" . meow-grab)
;       '("m" . meow-left)
;       '("M" . meow-left-expand)
;       '("i" . meow-right)
;       '("I" . meow-right-expand)
;       '("j" . meow-join)
;       '("k" . meow-kill)
;       '("l" . meow-line)
;       '("L" . meow-goto-line)
;       '("h" . meow-mark-word)
;       '("H" . meow-mark-symbol)
;       '("n" . meow-next)
;       '("N" . meow-next-expand)
;       '("o" . meow-block)
;       '("O" . meow-to-block)
;       '("p" . meow-yank)
;       '("q" . meow-quit)
;       '("r" . meow-replace)
;       '("s" . meow-insert)
;       '("S" . meow-open-above)
;       '("t" . meow-till)
;       '("u" . meow-undo)
;       '("U" . meow-undo-in-selection)
;       '("v" . meow-search)
;       '("w" . meow-next-word)
;       '("W" . meow-next-symbol)
;       '("x" . meow-delete)
;       '("X" . meow-backward-delete)
;       '("y" . meow-save)
;       '("z" . meow-pop-selection)
;       '("'" . repeat)
;       '("<escape>" . ignore)))
;
;  (require 'meow)
;  (meow-setup)
;  (meow-global-mode 1)

Evil

(use-package evil
  :ensure t
  :config
  (evil-mode 1))
(use-package undo-tree
  :ensure t
  :after evil
  :diminish
  :config
  (evil-set-undo-system 'undo-tree)
  (global-undo-tree-mode 1))
(use-package evil-org
  :ensure t
  :after org
  :hook (org-mode . (lambda () evil-org-mode))
  ;; :config
  ;; (require 'evil-org-agenda)
  ;; (evil-org-agenda-set-keys)
  )

(use-package evil-collection
  :after evil
  :ensure t
  :diminish evil-collection-unimpaired-mode
  :init
  (setq evil-want-C-u-scroll t)
  :config
  (evil-collection-init)
  (setq evil-want-C-i-jump t
        evil-respect-visual-line-mode t))

(use-package evil-commentary
  :config
  (evil-commentary-mode))

Web browsing and youtube

;; Using straight:
;;(use-package empv
;;  :straight (:host github :repo "isamert/empv.el"))

(use-package empv)

(setq empv-radio-channels
      '(("SomaFM - Groove Salad" . "http://www.somafm.com/groovesalad.pls")
        ("SomaFM - Drone Zone" . "http://www.somafm.com/dronezone.pls")
        ("SomaFM - Sonic Universe" . "https://somafm.com/sonicuniverse.pls")
        ("SomaFM - Metal" . "https://somafm.com/metal.pls")
        ("SomaFM - Vaporwaves" . "https://somafm.com/vaporwaves.pls")
        ("ADHD 1" . "https://www.youtube.com/watch?v=CmMrm4BpQHU")
        ("WREK FM" . "http://streaming.wrek.org:8000/wrek_live-128kb.m3u")
        ))
;; Todo: lainchan? adhd music stream from youtube? wuog? wrekFM? does freeside have streaming radio?
(with-eval-after-load 'embark (empv-embark-initialize-extra-actions))
(add-to-list 'empv-mpv-args "--ytdl-format=best")
(add-to-list 'empv-mpv-args "--save-position-on-quit")
(add-hook 'empv-init-hook #'empv-override-quit-key)

(setq empv-invidious-instance "https://invidious.projectsegfau.lt/api/v1")

Todo

General

  • Refactor to use use-package more effectively
    • Use :mode and :config like here
    • Set things which should be set before package install in :init
  • if emacs.d/snippets doesn’t exist, clone snippets repo

Terminals

  • More terminals
  • emacs-fish-completions
  • setup terminals to complete to vertico
  • starship prompts

New functions

  • Git-time-machine-or-vc-region-history

Note taking

  • deft
  • org : bullet

One-offs

  • hl-todo
  • highlight tabs and newlines
  • Vertico backwards delete
  • alphabetical keybindings
  • brighter modeline
  • ignore case in imenu
  • improve which-key visibility
  • fix lisp autoformat in org-mode code blocks

LSP stuff

  • look into treemacs integrations
  • consider eglot
  • dap-mode

Completion

  • dabbrev-expand or company on c-n c-p
  • c-N c-P for expand full line
  • yasnippet
  • company-mode to corfu (or vertico completions? or cofu-in-vertico?)
  • get vertico as completion box

Keybindings

  • put general bindings in separate keymaps in separate sections
  • map hyper bindings for buffer nav and jumping
  • evil-org
  • map alignment functions in ‘code’section
  • define global meow-left and vim-left, swich contextually
  • fix c-u in evil mode
  • fix tab key
  • window size functions, winner undo/redo
  • map default-text-scale operations
  • function for ‘update roam / deft / snippets git repo’

New plugins

  • tramp
  • k8s
  • docker
  • terraform
  • straight?
  • embark?
  • emms, emms-play-url binding (youtube view)
  • consult-lsp (better for errors than treemacs integrations probably)
  • srht.el
  • affe
  • gnus
  • explain-pause

Appearance switches

  • fonts
  • random theme
  • org mode bullets / pretty / etc

Research

  • What does C-d do when in a minibuffer for vertico?
  • embark
  • ace-window / embark integration

System crafters videos

Clean up Org Roam

  • Major mode notes master node

Snips from old config

ez pz

  (setq fancy-splash-image
        (concat doom-private-dir "sink.png"))  ;; the best emacs logo
  (load-theme 'doom-oceanic-next t)
  (setq lsp-lens-enable nil)                   ;; gets rid of the little '0 references' note from lsp

  ;; disable mac default bindings
  ;; TODO: what?
  (when IS-MAC
      (setq mac-pass-command-to-system nil))


(setq org-roam-directory "~/notes/roam")

requires extra config

(with-eval-after-load 'ox-latex
  (add-to-list 'org-latex-classes
               '("org-plain-latex"
                 "\\documentclass{article}
                [NO-DEFAULT-PACKAGES]
                [PACKAGES]
                [EXTRA]"
                 ("\\section{%s}" . "\\section*{$s}")
                 ("\\subsection{%s}" . "\\subsection*{$s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{$s}")
                 ("\\paragraph{%s}" . "\\paragraph*{$s}")
                 ("\\subparagraph{%s}" . "\\subparagraph*{$s}"))))

A bit heavy…

(use-package! org
  :config
  (add-to-list 'org-agenda-files "~/cal/personal.org")
  )
(setq plstore-cache-passphrase-for-symmetric-encryption t)
(setq org-icalendar-timezone "America/New_York")
(setq org-caldav-save-directory "~/cal")
(setq org-caldav-calendars
      '((:calendar-id "1ea706f0-8d7e-f884-e837-fe17fbd8c855"
         :url "http://cal.dump.town/admin"
         :files ("~/cal/personal.org")
         :inbox "~/cal/from-personal.org")
        )
      )