dotfiles/.config/emacs/packages.org

47 KiB
Raw Permalink Blame History

Packages.El Literate:

;; NOTE:
;; This file is generated from packages.org

Helpers

Helper variables adopted from Doom Emacs I'm not sure if I'll ever use.

(defconst IS-MAC      (eq system-type 'darwin))
(defconst IS-LINUX    (memq system-type '(gnu gnu/linux gnu/kfreebsd berkeley-unix)))
(defconst IS-WINDOWS  (memq system-type '(cygwin windows-nt ms-dos)))
(defconst IS-BSD      (memq system-type '(darwin berkeley-unix gnu/kfreebsd)))
(defconst HAS-EMACS28+    (> emacs-major-version 27))
(defconst HAS-EMACS29+    (> emacs-major-version 28))
(defconst HAS-DYNMODULES  (featurep 'dynamic-modules))
(defconst HAS-NATIVECOMP  (featurep 'native-compile))

Theme

I sometimes switch between these (sub-headings). To switch, set :tangle yes/no.

(set-face-attribute 'completions-first-difference nil :weight 'normal)

(defvar ./corfu-bar-color "gray20" ;; Default value is for dark themes
  "Color for the corfu scroll bar.")
(defvar ./cursor-color "white"
  "Color for cursor")
(defvar ./theme-type "dark"
  "Dark or light")

TODO:

  • Put all variable declarations in init.org
  • These theme configurations, define them as modules so I can easily switch between them at run time (and load my customizations for each theme together)
  • Also maybe (if possible) if I were to switch at run time ensure previous theme setting aren't retained.

Nord

Used by Rougier's NANO-emacs. The dark theme is kinda nice, like dracula but less purple, no pink, more cyan. But the muted colors are too unreadable (such as docstrings or comments). TODO: Customize the lower-contrast nord colors to be more readable.

(use-package nord-theme
  :config
  (load-theme 'nord t)
  )

EF-themes (ef-light)

Dazzling, tacky, elegant light theme.

(use-package ef-themes
  ;; :init
  ;; (set-face-attribute 'ef-themes-ui-variable-pitch nil :inherit 'variable-pitch)
  :config
  (load-theme 'ef-light t)
  (set-face-attribute 'font-lock-builtin-face nil :weight 'normal)
  (set-face-attribute 'font-lock-keyword-face nil :weight 'normal)
  (set-face-attribute 'font-lock-variable-name-face nil :foreground "black")
  (set-face-attribute 'font-lock-property-name-face nil :foreground "black")
  (set-face-attribute 'font-lock-constant-face nil :foreground "black")
  (set-face-attribute 'font-lock-string-face nil :foreground "dark green")

  (set-face-attribute 'window-divider nil :foreground "gray90")
  (set-face-attribute 'window-divider-first-pixel nil :foreground "white")
  (set-face-attribute 'window-divider-last-pixel nil :foreground "white")

  (setq ./corfu-bar-color "gray80")

  (set-face-attribute 'org-link nil :foreground "DodgerBlue3" :underline t)
  )

EF-themes (ef-elea-dark)

Current preferred dark theme.

The pink accent and the brownish background reminds me of gruvbox, but the purple and the green makes it look different. Overall it's quite ugly; and that is the beauty of it!

(use-package ef-themes
  :config
  (load-theme 'ef-elea-dark t)
  (set-face-attribute 'cursor nil :background "white")
  (set-face-attribute 'font-lock-builtin-face nil :weight 'normal)
  (set-face-attribute 'font-lock-keyword-face nil :weight 'normal)
  (set-face-attribute 'font-lock-variable-name-face nil :foreground "white")
  (set-face-attribute 'font-lock-property-name-face nil :foreground "white")
  (set-face-attribute 'font-lock-constant-face nil :foreground "white")

  (set-face-attribute 'window-divider nil :foreground "gray20")
  (set-face-attribute 'window-divider-first-pixel nil :foreground "black")
  (set-face-attribute 'window-divider-last-pixel nil :foreground "black")
)

Timu Spacegrey (dark)

This theme has partial zenburn vibes (orange) but less brown.

NGL it's so cute. Primary accent is like light brownish orange yet user-interaction accents use pink. :p

This was my second ever emacs preferred theme (it was zenburn first then this).

(use-package timu-spacegrey-theme
  :after (org corfu vertico)
  :config
  ;;(load-theme 'modus-vivendi t)
  (load-theme 'timu-spacegrey t)

  (defface ./theme-completion-popup
    '((t :inherit 'fixed-pitch))
    "Face for completion popup.")

  (defface ./theme-completion-selected
    '((t :background "gray25" :weight normal))
    "Face for selected completion item.")

  (set-face-attribute 'corfu-default nil :inherit './theme-completion-popup)
  (set-face-attribute 'corfu-current nil :inherit './theme-completion-selected :weight 'normal)
  (set-face-attribute 'corfu-bar nil :background "gray40")

  (set-face-attribute 'vertico-current nil :inherit './theme-completion-selected)
  ;;(set-face-attribute 'line-number nil :background "bg")
  ;; Color from modulus vivendi
  (set-face-attribute 'show-paren-match nil :background "#6f3355")
  (set-face-attribute 'cursor nil :background "textcolor")

  (set-face-attribute 'org-link nil :foreground "deep sky blue" :underline t)

  :init
  (setq timu-spacegrey-mode-line-border t)
  (setq timu-spacegrey-org-intense-colors t
        timu-spacegrey-scale-org-document-title 1.7
        timu-spacegrey-scale-org-document-info 1.4
        timu-spacegrey-scale-org-level-1 1.6
        timu-spacegrey-scale-org-level-2 1.3
        timu-spacegrey-scale-org-level-3 1.2)
)

XXX: Somehow putting the org scale settings in :init works? And putting it in :config doesn't work?!

Common settings for themes

(setq ./theme-type (symbol-name (frame-parameter nil 'background-mode)))
(setq ./cursor-color (if (string= ./theme-type "dark") "white" "black"))
(set-face-attribute 'cursor nil :background ./cursor-color)
;; TODO: Do this for window divider and corfu UI items, and magit diff backgrounds.
;; and org-link

Tab bar

https://www.gnu.org/software/emacs/manual/html_node/emacs/Tab-Bars.html

This makes the tabs in the tab bar fill the entire frame width, similar to qutebrowser.

(setq tab-bar-auto-width-max nil)

Go to *scratch* buffer when opening new tabs, like browsers. I guess!

(setq tab-bar-new-tab-choice "*scratch*")

Slightly more contrast

;; FIXME: Doesn't work
(set-face-attribute 'tab-bar-tab-inactive nil :background "textBackgroundColor")

Tabs in emacs appears to be similar to Vim, where each tab can hold window split layouts.

Default key-binds

Tab bar keys have prefix C-x t, use which-key to explore the options from there.

Switching tabs can be done with C-TAB and S-C-TAB

Evil

My setup allows using Vim bindings in normal "mode" and emacs bindings in insert "mode".

https://stackoverflow.com/questions/25542097/

I previously used this setting for achieve it:

;;  Use emacs keybindings in insert state 😱
(setq evil-disable-insert-state-bindings t)

It worked, but the bindings I've set for evil-insert-state-map elsewhere didn't take effect, so I opted for an alternative solutions from the stackoverflow question.

Below is the use-package block for evil. Noweb references are used, which are defined later on in this section.

(use-package evil
  :demand t
  :init
  <<evil-init>>

  :config
  <<evil-config>>

  <<evil-maps>>

  (defun ./evil-off ()
    "Call `turn-off-evil-mode' and show a message"
    (interactive)
    (turn-off-evil-mode)
    (message "Evil mode is turned off"))

  ;; Disable evil in some modes
  (dolist (mode '(elpaca-ui-mode
                  dired-mode
                  magit-mode))
    (evil-set-initial-state mode 'emacs))

  (evil-mode 1)
  )

Turning off evil mode doesn't seem to work so for elpaca hook I decided to just enter insert instead.

This allows Elpaca's shortcut keys to work, eg, q, I, s.

Init

These values must be set before evil load for them to take into effect.

  • Using evil-search allows search results to be kept highlighted when using /. I-search is still available in C-s in emacs state (see /hedy/dotfiles/src/branch/master/.config/emacs/Consult).
  • Still unsure whether to stick to emacs regexp or use vim for evil-related searches, but this is adopted from doom.
(setq evil-search-module 'evil-search
      evil-ex-search-vim-style-regexp t)

These are subject to change (REVIEW), adopted from doom.

(setq evil-ex-visual-char-range t  ; column range for ex commands
      evil-mode-line-format 'nil
      ;; more vim-like behavior
      evil-symbol-word-search t)

Set cursor to indicate the evil state. Generally bar means it's writable, box means movable, hollow means special.

(setq evil-default-cursor 'box
      evil-normal-state-cursor 'box
      evil-emacs-state-cursor  'bar
      evil-insert-state-cursor 'bar
      evil-visual-state-cursor 'hollow)

These are copied from doom.

      ;; Only do highlighting in selected window so that Emacs has less work
      ;; to do highlighting them all.
(setq evil-ex-interactive-search-highlight 'selected-window
      ;; It's infuriating that innocuous "beginning of line" or "end of line"
      ;; errors will abort macros, so suppress them:
      evil-kbd-macro-suppress-motion-error t)

Adopt vim's C-u and C-d scrolling

(setq evil-want-C-u-scroll t) ; must be set in :init

I read somewhere that you can access X-Selection in linux from a vim register, forgot which one of + or = it was. Well that's kinda neat, but how would it be useful? If you want to paste, you can copy. Unless you want to paste two things, so you copy one, and leave the selection on another, so you can do "+p for one and "=p for the other?

Oh well, I won't need that here (and possibly don't want that either, I'd like my clipboard to only contain things I explicitly copied).

(setq evil-visual-update-x-selection-p nil)

Set < and > to shifting 2 spaces, this allows adding up shifts to do 4 spaces.

Odd-numbered space indents is, well, um, weird (like 3). Sure if you can't decide between 2 and 4, but if you can't decide you can flip a coin, enforce it in .editorconfig, or use tabs and less the viewer decide!

Indenting by 1 space possibly only exist in lisp where the entire program is a rather obtuse array-like structure, and we tend to align arguments that span multiple lines by the function name, which means any arbitrary indent width is possible. Well, for that I will be relying on my editor to automatically indent for me. So in conclusion, setting shift-width to 2 is the best option as of now.

TODO: Like my neovim config, keep visual selection after shifting width.

(setq evil-shift-width 2)

Allow evil motion keys to go across lines. If point is at beginning of line, and I use left arrow or h, point will then be at the end of the previous line.

(setq evil-cross-lines t)

Set undo system to allow redos.

TODO:

  • Save undos in undodir like vim, so when opening new files there's undo data
  • Should I use undo-tree?
;; REVIEW: Is this needed if evil-redo-function is set?
(setq evil-undo-system 'undo-redo)  ;; 'undo-redo from Emacs 28
(setq evil-undo-function 'undo)
(setq evil-redo-function 'undo-redo)

Maps

Misc maps

This allows me to use emacs motion key bindings in evil insert mode. Super useful best of both worlds!

;; Emacs keybindings in evil insert state - must be set in :config
(setq evil-insert-state-map (make-sparse-keymap))
(define-key evil-insert-state-map (kbd "<escape>") 'evil-normal-state)
(define-key evil-emacs-state-map (kbd "<escape>") 'evil-normal-state)

C-e and C-y are already bound in emacs state, so I use shift of them instead.

(define-key evil-insert-state-map (kbd "S-C-e") 'evil-scroll-line-down)
(define-key evil-emacs-state-map (kbd "S-C-e") 'evil-scroll-line-down)
(define-key evil-insert-state-map (kbd "S-C-y") 'evil-scroll-line-up)
(define-key evil-emacs-state-map (kbd "S-C-y") 'evil-scroll-line-up)

S-C-v/c for clipboard: This is a solution I found on Doom Emacs forum. The problem was that if I use the system clipboard shortcut to copy things elsewhere, and I go to emacs, edit some text with d, y etc, then if I use the system clipboard shortcut to paste what I copied earlier, results from d=/=y are pasted instead. This setting bindings different keys for pasting from system clipboard within emacs - Ctrl shift C and V. It may be a little unergonomic going between these

;; Don't put vim yanks into system clipboard
;; But use shift C-v / C-c to paste/copy from system clipboard instead
(setq select-enable-clipboard nil)
(global-set-key (kbd "S-C-c") #'clipboard-kill-ring-save)
(global-set-key (kbd "S-C-v") #'clipboard-yank)

(global-set-key (kbd "<escape>") 'keyboard-escape-quit)

Add some useful keys for command line (:) and global buffer switching using evil functions.

;; FIXME
(evil-set-leader nil (kbd "S-C-SPC")) ;; C-SPC is mark set
(evil-set-leader 'normal (kbd "SPC"))

(define-key evil-command-line-map "\C-a" 'move-beginning-of-line)
(define-key evil-command-line-map "\C-e" 'move-end-of-line)
;; (define-key evil-command-line-map "\C-d" nil t)
;; (define-key evil-command-line-map "\C-l" nil t)

(global-set-key (kbd "s-<left>") #'previous-buffer)
(global-set-key (kbd "s-<right>") #'next-buffer)
(global-set-key (kbd "S-s-<down>") #'evil-window-down)
(global-set-key (kbd "S-s-<up>") #'evil-window-up)
(global-set-key (kbd "S-s-<left>") #'evil-window-left)
(global-set-key (kbd "S-s-<right>") #'evil-window-right)

Leader maps

This solution for binding leader prefixes is found on evil-guide: https://github.com/noctuid/evil-guide?tab=readme-ov-file#leader-key

(defvar ./leader-map (make-sparse-keymap)
  "Keymap for leader shortcuts")
(define-key evil-normal-state-map (kbd "SPC") ./leader-map)
(define-key ./leader-map "q" #'evil-quit)
(define-key ./leader-map "w" #'save-buffer)
(define-key ./leader-map "x" #'evil-save-modified-and-close)
(define-key ./leader-map "1" #'delete-other-windows)

(defvar ./leader-buffer-map (make-sparse-keymap)
  "Keymap for leader shortcuts for buffers")
(define-key ./leader-map "b" ./leader-buffer-map)
(define-key ./leader-buffer-map "b" #'consult-buffer)
(define-key ./leader-buffer-map "s" #'save-buffer)
(define-key ./leader-buffer-map "d" #'evil-delete-buffer)

(defvar ./leader-file-map (make-sparse-keymap)
  "Keymap for leader shortcuts for files")
(define-key ./leader-map "f" ./leader-file-map)
(define-key ./leader-file-map "f" #'find-file)
(define-key ./leader-file-map "r" #'consult-recent-file)
(define-key ./leader-file-map "p" #'project-find-file)

(defvar ./leader-frame-map (make-sparse-keymap)
  "Keymap for leader shortcuts for frames")
(define-key ./leader-map "F" ./leader-frame-map)
(define-key ./leader-frame-map "q" #'delete-frame)
(define-key ./leader-frame-map "d" #'delete-frame)
(define-key ./leader-frame-map "u" #'undelete-frame)
(define-key ./leader-frame-map "R" #'rename-frame)
(define-key ./leader-frame-map "o" #'other-frame)
(define-key ./leader-frame-map "c" #'clone-frame)

Misc

Other plugins (or apps) with not that much configuration.

(use-package elpher)
;; eww is part of emacs now?!!
;;(use-package eww)

(use-package visual-fill-column
  :init
  (setq-default visual-fill-column-center-text t))

(use-package imenu-list
  :config
  (setq imenu-list-auto-resize t)
  ;; Auto-update Ilist buffer
  :hook (imenu-list-major-mode . (lambda ()
                                   (imenu-list-minor-mode 1)
                                   (visual-line-mode 1)  ;; REVIEW
                                   (display-line-numbers-mode -1)
                                   (evil-insert-state 1))))

(use-package math-symbol-lists
  :after cape
  :config
  ;; This is actually for C-\, then select input "math",
  ;; then the Ω will show in the status bar.
  (quail-define-package "math" "UTF-8" "Ω" t)
  ;; (quail-define-rules ; add whatever extra rules you want to define here...
  ;;  ("\\from"    #X2190)
  ;;  ("\\to"      #X2192)
  ;;  ("\\lhd"     #X22B2)
  ;;  ("\\rhd"     #X22B3)
  ;;  ("\\unlhd"   #X22B4)
  ;;  ("\\unrhd"   #X22B5))
  (mapc (lambda (x)
          (if (cddr x)
              (quail-defrule (cadr x) (car (cddr x)))))
        (append math-symbol-list-basic math-symbol-list-extended))
  )

Maybe I'll figure out a better way to organize list of modes where display-line-numbers-mode should be disabled.

;; (use-package help
;;   :elpaca nil
;;   :hook
;;   ((help-mode Custom-mode) . (lambda () (display-line-numbers-mode -1)))
;;   )

Dang Custom-mode is actually capitalized

Dired

;; (use-package dired
  ;; :elpaca nil
  ;; :config
  (setq delete-by-moving-to-trash t
        ;; Emacs 29
        dired-make-directory-clickable t
        dired-mouse-drag-files t
        )

  ;;:hook
  ;; FIXME: replicate evil state cursor style switching here
  (defun ./cursor-toggle-readonly ()
    (if buffer-read-only
        (progn
          (setq cursor-type 'box)
          (set-cursor-color "orange"))
      (setq cursor-type 'bar)
      (set-cursor-color ./cursor-color)))

  (add-hook 'read-only-mode-hook #'./cursor-toggle-readonly)
  (add-hook 'dired-mode-hook (lambda () (hl-line-mode) (./cursor-toggle-readonly)))
  ;; )

Minibuffer

Enjoy emacs' editting key chords while there's still a glimmer of space in your emacs that forces you to use it, child.

(add-hook 'minibuffer-setup-hook (lambda () (setq cursor-type 'bar)))
(add-hook 'minibuffer-exit-hook (lambda () (setq cursor-type 'box)))

Wrap region

This plugin gives you true IDE-like behaviour of selecting some text, press " then it'll wrap your selection with quotes.

It enables this for quotes and brackets by default, below I've added some more useful wrappers, some of which are also suggested from the wrap region README.

(use-package wrap-region
  :config
  (wrap-region-add-wrappers
   '(("/* " " */" "#" (java-mode javascript-mode css-mode))
     ("`" "`" nil (markdown-mode org-mode))
     ("=" "=" nil (org-mode))
     ("~" "~" nil (org-mode))
     ("*" "*" nil (markdown-mode org-mode))))
  :hook
  ((org-mode markdown-mode) . wrap-region-mode)
)

Magit

(use-package magit)

Breadcrumb

By the owner of both eglot and yasnippet: breadcrumb context in your headerline that uses project.el or imenu in that order!

And yes you can even click on the breadcrumb components to jump to things like imenu.

(use-package breadcrumb
  :diminish breadcrumb-mode
  :init
  (breadcrumb-mode 1))

Vertico

(use-package vertico
  :init
  (vertico-mode)
  ;; Grow and shrink the Vertico minibuffer
  (setq vertico-resize t)
  ;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
  (setq vertico-cycle t)
  :hook
  ;; For find-file, remove old file path if I start typing a new one
  ('rfn-eshadow-update-overlay-hook . #'vertico-directory-tidy)
  )

(use-package orderless
  :init
  (setq completion-styles '(orderless)
        completion-category-defaults nil
        completion-category-overrides '((file (styles partial-completion)))))
;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
  :elpaca nil
  :init
  (savehist-mode))
;; Pasted from vertico
(use-package emacs
  :elpaca nil
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; Alternatively try `consult-completing-read-multiple'.
  (defun crm-indicator (args)
    (cons (concat "[CRM] " (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)
  ;; From corfu
  ;; TAB cycle if there are only few candidates
  (setq completion-cycle-threshold 3)

  ;; Emacs 28: Hide commands in M-x which do not apply to the current mode.
  ;; Corfu commands are hidden, since they are not supposed to be used via M-x.
  ;; (setq read-extended-command-predicate
  ;;       #'command-completion-default-include-p)
)

Marginalia shows description of each candidate in minibuffer completion next to candidates.

(use-package marginalia
  :diminish
  :config
  (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  (marginalia-mode 1)
)

Consult

(use-package consult
  :config
  (global-set-key (kbd "C-s") 'consult-line)
  (define-key evil-insert-state-map (kbd "C-s") 'isearch-forward)
  (define-key evil-emacs-state-map (kbd "C-s") 'isearch-forward)
  (global-set-key (kbd "C-c g") 'consult-org-heading)
  (global-set-key (kbd "C-x C-b") 'consult-buffer)
  ;; Doesn't work?
  (global-set-key [?\C-\t] 'consult-buffer)
  (define-key minibuffer-local-map (kbd "C-r") 'consult-history)

  (setq completion-in-region-function #'consult-completion-in-region)
  )

Corfu

Note that some color settings are set in /hedy/dotfiles/src/branch/master/.config/emacs/Theme

(use-package corfu
  :custom
  (corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
  ;; Default is M-SPC, if M-SPC is bound like I have on my Mac (Alfred) S-M-SPC also works
  ;;(corfu-separator ?\s) ;; Orderless separator
  ;; separator: Quit at boundary if no `corfu-separator' inserted
  (corfu-quit-at-boundary 'separator)
  ;; separator: only stay alive if no match and `corfu-separator' inserted
  (corfu-quit-no-match 'separator)
  ;; Don't change what I typed to what I selected when previewing completions
  (corfu-preview-current nil)
  (corfu-preselect 'first)
  ;; Default = #'insert. Options: quit, nil
  ;;(corfu-on-exact-match nil)
  ;; Prevent last/first item being hidden behind windows
  ;; FIXME: Doesn't work
  (corfu-scroll-margin 2)
  (corfu-right-margin-width 2)

  ;; Enable Corfu only for certain modes.
  ;; :hook ((prog-mode . corfu-mode)
  ;;        (shell-mode . corfu-mode)
  ;;        (eshell-mode . corfu-mode))

  ;; FIXME: doesn't work: evil insert/emacs keybinds takes higher precendence it seems
  (define-key corfu-map (kbd "<escape>") 'corfu-quit)

  :custom-face
  (corfu-border ((t (:background "gray20" :weight bold))))
  (corfu-default ((t (:inherit fixed-pitch))))

  :init
  ;; Recommended: Enable Corfu globally.
  ;; This is recommended since Dabbrev can be used globally (M-/).
  ;; See also `global-corfu-modes'.
  (global-corfu-mode)
  (corfu-popupinfo-mode 1)

  :config
  (setq corfu-bar-width 0.8)
  (set-face-attribute 'corfu-bar nil :background ./corfu-bar-color)
  (defun corfu-enable-always-in-minibuffer ()
    "Enable Corfu in the minibuffer if Vertico/Mct are not active."
    (unless (or (bound-and-true-p mct--active)
                (bound-and-true-p vertico--input)
                (eq (current-local-map) read-passwd-map))
      ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion
      (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
                  corfu-popupinfo-delay '(0 . 0)) ;; Use popupinfo in minibuffer too, why not?
      (corfu-mode 1)))
  (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)

  (setq corfu-popupinfo-delay '(0 . 0))
)

Kind-icon + Corfu

This is like one of those (few) times that I've cherished Custom's convenience.

(use-package kind-icon
  :after corfu
  :custom
  (kind-icon-mapping ;; These are fetched (and cached) from pictogrammers.com/library/mdi
    '((array "ar" :icon "code-brackets" :face font-lock-type-face)
      (boolean "b" :face font-lock-builtin-face)
      (class "C" :face font-lock-type-face) ;; family-tree could be used. but too dense
      (color "#" :icon "palette" :face success)
      (command ">_" :face default)
      (constant "cn" :icon "lock-remove-outline" :face font-lock-constant-face)
      (constructor "C+" :icon "plus-circle-multiple" :face font-lock-function-name-face)
      (enummember "em" :icon "order-bool-ascending-variant" :face font-lock-builtin-face)
      (enum-member "em" :icon "order-bool-ascending-variant" :face font-lock-builtin-face)
      (enum "e" :icon "format-list-bulleted-square" :face font-lock-builtin-face)
      (event "ev" :icon "lightning-bolt-outline" :face font-lock-warning-face)
      (field "fd" :face font-lock-variable-name-face)
      (file "F" :icon "file-document-outline" :face font-lock-string-face)
      (folder "D" :icon "folder" :face font-lock-doc-face)
      (interface "if" :icon "application-brackets-outline" :face font-lock-type-face)
      (keyword "kw" :face font-lock-keyword-face)
      (macro "mc" :icon "lambda" :face font-lock-keyword-face)
      (magic "ma" :icon "shimmer" :face font-lock-builtin-face)
      (method "me" :face font-lock-function-name-face)
      (function "f" :icon "function" :face font-lock-function-name-face)
      (module "mo" :icon "package-variant-closed" :face font-lock-preprocessor-face)
      (numeric "0" :icon "numeric" :face font-lock-builtin-face)
      (operator "÷" :icon "division" :face font-lock-comment-delimiter-face)
      (param "pa" :icon "cog-outline" :face default)
      (property "pr" :icon "wrench" :face font-lock-variable-name-face)
      (reference "rf" :icon "library" :face font-lock-variable-name-face)
      (snippet "S" :face font-lock-string-face)
      (string "\"" :icon "text-box" :face font-lock-string-face)
      (struct "{}" :icon "code-braces" :face font-lock-variable-name-face)
      (text " " :face font-lock-doc-face) ; text-short could be used
      (typeparameter "tp" :icon "format-list-bulleted-type" :face font-lock-type-face)
      (type-parameter "tp" :icon "format-list-bulleted-type" :face font-lock-type-face)
      (unit "u" :icon "square-rounded-outline" :face font-lock-constant-face)
      (value "vl" :icon "plus-circle-outline" :face font-lock-builtin-face)
      (variable "v" :face font-lock-variable-name-face)
      (t "?" :face font-lock-warning-face)))
    (kind-icon-blend-background nil)
  :custom-face
  (kind-icon-default-face ((t (:background nil))))

  :config
  ;;(setq kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

Cape + Corfu

With references from System Crafter's crafted-emacs configuration

(use-package cape
  ;;:after math-symbol-lists
  :config
  ;; Add useful defaults completion sources from cape
  ;; (add-to-list 'completion-at-point-functions #'cape-file)
  ;; ;;(add-to-list 'completion-at-point-functions #'cape-dabbrev)
  ;;(add-to-list 'completion-at-point-functions #'cape-tex)
  (add-to-list 'completion-at-point-functions #'cape-emoji)

  ;; Silence the pcomplete capf, no errors or messages!
  ;; Important for corfu
  (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent)

  ;; Ensure that pcomplete does not write to the buffer
  ;; and behaves as a pure `completion-at-point-function'.
  (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)

  (define-key evil-insert-state-map (kbd "C-x C-f") 'cape-file)
  (define-key evil-insert-state-map (kbd "C-x C-d") 'cape-dict)
  (define-key evil-insert-state-map (kbd "C-x C-w") 'cape-dabbrev)
  (define-key evil-insert-state-map (kbd "C-x C-:") 'cape-emoji)

  (cape-char--define math "math" ?\\)
  (add-to-list 'completion-at-point-functions #'cape-math)
  (define-key evil-insert-state-map (kbd "C-x C-$") 'cape-math)

  :hook (eshell-mode-hook . (lambda () (setq-local corfu-quit-at-boundary t
                                                   corfu-quit-no-match t
                                                   corfu-auto nil)
                              (corfu-mode)))

)

I disabled adding dabbrev to CAPF to prevent corfu-candidate-overlay (see below) from suggesting arbitrary text completions when I'm in comments or strings or whatever. It's annoying.

Corfu Candidate Overlay

It's like how copilot gives you a completion after your cursor… but this is corfu! (first candidate)

Also like fish's autosuggestion.

(use-package corfu-candidate-overlay
  :config
  (corfu-candidate-overlay-mode 1) ;; This is global
  (set-face-attribute 'corfu-candidate-overlay-face nil :foreground "dim grey")
  ;; Use TAB to accept a completion, how cool is that!
  <<insert-state-tab-cmd>>
  (define-key evil-insert-state-map (kbd "TAB") './insert-state-tab)
)

The function below is the handler for the TAB key in evil insert state. The gist of what it does starts in the (if at-heading [...]) block. The extra code before which is explained in the comment.

The last time I used Doom, it doesn't support using org-cycle if point is at the end of line on an org heading. I have to move it ON the heading text for it to org-cycle.

The first snippet below was my first attempt at this issue, before I checked the source code for corfu candidate overlay to obtain code for checking whether CAP is possible at point. The first snippet sort of works but is not as good, see the excessive comments.

The second snippet is the tangled one, it works (for now).

(defun ./insert-state-tab ()
  "Handle TAB key in insert state.

Confirm candidate overlay or call `org-cycle' depending on position of
current point.

If it is at an org heading, or at the end of line that contains a
folded org heading, then `org-cycle' is called. Otherwise
`corfu-candidate-overlay-complete-at-point'."
  (interactive)
    (if (org-at-heading-p)
        (org-cycle)
      (let ((current-char (buffer-substring-no-properties (point) (+ (point) 1)))
            (current-line (buffer-substring-no-properties
                           (line-beginning-position) (line-end-position))))
        (if (and (string= current-char "\n") (org-invisible-p2))
            ;; If point is at the very end of an org heading
            ;; `org-at-heading-p' returns nil so I have to check it
            ;; another way.
            ;; I'm honestly not sure if there is a case where the
            ;; condition above evaluates to true but we don't actually
            ;; want to `org-cycle', so I added a message.
            (progn
              ;;(end-of-line)
              ;; `end-of-line' actually moves point to position BEFORE
              ;; the ellipsis char where as evil's end of line moves
              ;; it after (as with if position is selected with mouse,
              ;; click at the end of line of folded org heading).
              ;; Initially I wanted to use this to put point in the
              ;; "visible" end of line position and use `org-cycle',
              ;; which should work (when called interactively). But
              ;; for some reason it didn't work when using this
              ;; function so I decided to use `evil-open-fold'
              ;; instead.
              ;; 
              ;; Surprisingly evil's fold on org works even if point
              ;; is on the (weird) end of folded heading line
              ;; position, which as mentioned above is where
              ;; `org-at-heading-p' returns nil.
              ;;
              ;; TODO: Possibly a bug with org itself?
              (save-excursion ;; Point is still moved out of the heading line!
                (evil-open-fold)
                (message "Assumed to be at end of folded org heading line. \
  If org-cycle is unwanted here. Please edit ./insert-state-tab function")))
          ;; Reaching here if '(and (string= [...]) [...])' not true.
          ;; REVIEW: A way to fix all the excessive comments above is
          ;; to have a way of determining whether corfu-candidate CAP
          ;; could act, rather than checking with org. If
          ;; candidate-overlay cannot act I could just call
          ;; `evil-open-fold' and not bother with `org-cycle' at all.
          (corfu-candidate-overlay-complete-at-point)
          ))
      ;; Is it possible with this implementation to add further
      ;; functionality to this TAB key? I need a way to check if
      ;; candidate-overlay is visible.
      ))

Even though it ends up with repeated corfu-candidate-overlay checks, it's a cleaner and easier to maintain (and extend) implementation than the one above.

(defun ./insert-state-tab ()
  "Handle TAB key in insert state.

If corfu-candidate-overlay's overlay is active, calls
`corfu-candidate-overlay--get-overlay-property', otherwise
`evil-toggle-fold'. See my packages.org for this section for why I
didn't use `org-cycle' here."
  (interactive)
  (if (overlayp corfu-candidate-overlay--overlay)
      (progn
        ;; This check is taken exactly from the implementation of
        ;; `corfu-candidate-overlay-complete-at-point's (as of
        ;; writing).
        (corfu-candidate-overlay--show)
        (if (and (overlayp corfu-candidate-overlay--overlay)
                 (not (string= (corfu-candidate-overlay--get-overlay-property 'after-string) "")))
            (corfu-candidate-overlay-complete-at-point)
          (if (string-match-p
               "^\*+ "
               (buffer-substring-no-properties
                (line-beginning-position)
                (line-end-position)))
          (evil-toggle-fold)
          (indent)
              )
          ))
          (if (string-match-p
               "^\*+ "
               (buffer-substring-no-properties
                (line-beginning-position)
                (line-end-position)))
          (evil-toggle-fold)
          (indent-for-tab-command)
    )))

Which-Key

(use-package which-key
  :diminish
  :config
  (which-key-setup-side-window-right)
  (which-key-mode 1))

Org

(use-package org
  :elpaca nil
  :config
  <<org-config>>

  ;; (dolist (item '( ;; Newline used to add prefix to tangled file
  ;;                <<org-font-attributes>>))
  ;;   (apply #'set-face-attribute item))
  (custom-theme-set-faces
   'user
   <<org-font-attributes>>
   )

  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (python . t)
     (lua . t)
     (js . t)))

  (defun org-babel-execute:nim (body params)
    "Execute a block of Nim code with org-babel."
    (let ((in-file (org-babel-temp-file "n" ".nim"))
          (verbosity (or (cdr (assq :verbosity params)) 0)))
      (with-temp-file in-file
        (insert body))
      (org-babel-eval
       (format "nim compile --verbosity=%d --run %s" verbosity
               (org-babel-process-file-name in-file))
       "")))

  (defun org-babel-execute:moonscript (body params)
    "Execute a block of MoonScript code with org-babel."
    (let ((in-file (org-babel-temp-file "m" ".moon")))
      (with-temp-file in-file
        (insert body))
      (org-babel-eval
       (format "moon %s" (org-babel-process-file-name in-file)) "")))

  :hook
  (org-mode . (lambda () (visual-line-mode 1)
                (variable-pitch-mode)
                (display-line-numbers-mode -1)))
  )

REVIEW:

Using set-face-attribute rather than custom-theme-set-faces doesn't work! Says org-indent invalid face…

Org font faces

'(org-block ((t (:inherit fixed-pitch))))
'(org-code ((t (:inherit (shadow fixed-pitch)))))
'(org-document-info ((t (:foreground "dark orange"))))
'(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
'(org-indent ((t (:inherit (org-hide fixed-pitch)))))
'(org-link ((t (:foreground "deep sky blue" :underline t))))
'(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
'(org-property-value ((t (:inherit fixed-pitch))) t)
'(org-block-begin-line ((t (:inherit (font-lock-comment-face fixed-pitch)))) t)
'(org-block-end-line ((t (:inherit (font-lock-comment-face fixed-pitch)))) t)
'(org-drawer ((t (:inherit fixed-pitch))) t)
'(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
'(org-table ((t (:inherit fixed-pitch))))
'(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
'(org-verbatim ((t (:inherit (shadow fixed-pitch)))))

Org config

These have noweb-ref "org-config" and are put in the :config of use-package above.

Enable indenting paragraphs under headings by default

(setq org-startup-indented t)

Don't indent stuff in SRC. They show up on exports and when viewers copy the entire SRC block the indents are also copied! (defaulted to 2 spaces).

(setq org-edit-src-content-indentation 0)

Indent sub-list items

(setq org-list-indent-offset 2)

Org superstar

Org superstar is like org-bullets but with additional customizations as well as styling plain lists

(use-package org-superstar
  :config
  (setq org-superstar-configure-like-org-bullets t)
  :hook
  (org-mode . (lambda () (org-superstar-mode 1))))

Org auto tangle

Especially useful for my literate emacs config.

(use-package org-auto-tangle
  :defer t
  :hook (org-mode . org-auto-tangle-mode)
  :config
  (setq org-auto-tangle-babel-safelist '(
                                         "~/.config/emacs/packages.org"
                                         "~/.config/emacs/init.org")))

Eglot & tree sitter

Some languages

(use-package lua-mode)
(use-package moonscript)
(use-package nim-mode)

Eglot

Eglot is now included in Emacs from version 29.

(use-package eglot
  :elpaca nil
  :defer t
  :hook
  ((python-ts-mode go-ts-mode lua-mode) . eglot-ensure)
)

Tree-sitter as well, but you must manually clone the treesitter repo and ./build for each language, the copy the output file to <user-emacs-directory>/tree-sitter/

I wrote a patch for ./build to have it automatically copy the resulting file into where I want:

diff --git a/build.sh b/build.sh
index 25b5c1e..75a01b3 100755
--- a/build.sh
+++ b/build.sh
@@ -5,6 +5,7 @@ set -e
 
 lang=$1
 topdir="$PWD"
+destdir=$2
 
 if [ "$(uname)" == "Darwin" ]
 then
@@ -151,3 +152,7 @@ mkdir -p "${topdir}/dist"
 cp "libtree-sitter-${lang}.${soext}" "${topdir}/dist"
 cd "${topdir}"
 rm -rf "${lang}"
+
+if [ -n $destdir ]; then
+    mv "$topdir/dist/libtree-sitter-$lang.$soext" $destdir/
+fi

After applying this patch (you can save the file as add-destdir.patch, then run git apply add-destdir.patch from within the cloned repo), you can then use ./build.sh python ../tree-sitter, which would build the tree sitter module for python, then copy the result into ../tree-sitter. This is if your tree-sitter repo is cloned at <user-emacs-directory>/tree-sitter-repo for example.

Configuration below enables tree-sitter mode for each major language mode I want to have tree-sitter for.

;; Open python files in tree-sitter mode.
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
(add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode))
(add-to-list 'auto-mode-alist
             '("\\.go\\'" . (lambda ()
                               (go-ts-mode)
                               )))
(add-to-list 'auto-mode-alist
             '("go.mod\\'" . (lambda ()
                               (go-mod-ts-mode)
                               )))

Diminish

Diminish allows us to use minor modes without showing it.

Calling diminish to specify the mode to hide, (or specify 2nd argument for the alternative display text).

;; FIXME
(use-package diminish
  :config
  (diminish 'buffer-face-mode)
  (diminish 'org-auto-tangle-mode)
  (diminish 'eldoc-mode)
  (diminish 'auto-revert-mode)
  (diminish 'visual-line-mode)
  (diminish 'org-indent-mode)
  (diminish 'subword-mode)
  )

Eshell

Significant portions of this section is credited to: https://github.com/howardabrams/hamacs/blob/main/ha-eshell.org

Opening files

(defun ./eshell-fn-on-files (fun1 fun2 args)
  "Call FUN1 on the first element in list, ARGS.
Call FUN2 on all the rest of the elements in ARGS."
  (unless (null args)
    (let ((filenames (flatten-list args)))
      (funcall fun1 (car filenames))
      (when (cdr filenames)
        (mapcar fun2 (cdr filenames))))
    ;; Return an empty string, as the return value from `fun1'
    ;; probably isn't helpful to display in the `eshell' window.
    ""))
(defun eshell/ff (&rest files)
  "find-file on first arg, find-file-other-window on rest"
  (./eshell-fn-on-files 'find-file 'find-file-other-window files))

(defun eshell/f (&rest files)
  "Edit one or more files in another window."
  (./eshell-fn-on-files 'find-file-other-window 'find-file-other-window files))

In case I somehow end up in (n)vi(m), I can possibly use my vim's <leader>q to quit, but still.

Oh yeah oopsie doopsie if I end up in nvim, since my leader there is SPC, same as doom emacs… Oh Noes!

(defalias 'eshell/emacs 'eshell/ff)
(defalias 'eshell/vi 'eshell/ff)
(defalias 'eshell/vim 'eshell/ff)
(defalias 'eshell/nv 'eshell/ff)
(defalias 'eshell/nvim 'eshell/ff)
(defun eshell/less (&rest files)
  "view-file-other-window"
  (view-file-other-window files))

(defalias 'eshell/more 'eshell/less)

Aliases

Some aliases >>> eshell-aliases-file

alias ll exa -lahg --git -t modified
alias clr clear 1
alias x exit
alias d dired $1

Kill window on exit https://stackoverflow.com/questions/51867693/emacs-eshell-kill-window-on-exit#51867960

(defun ./eshell-exit-with-window ()
  (when (not (one-window-p))
    (delete-window)))

(advice-add 'eshell-life-is-too-much :after './eshell-exit-with-window)

Useful functions

(defun eshell/do (&rest args)
  "Execute a command sequence over a collection of file elements.
Separate the sequence and the elements with a `::' string.
For instance:

    do chown _ angela :: *.org(u'oscar')

The function substitutes the `_' sequence to a single filename
element, and if not specified, it appends the file name to the
command. So the following works as expected:

    do chmod a+x :: *.org"
  (seq-let (forms elements) (-split-on "::" args)
    (dolist (element (-flatten (-concat elements)))
      (message "Working on %s ... %s" element forms)
      (let* ((form (if (-contains? forms "_")
                       (-replace "_" element forms)
                     (-snoc forms element)))
             (cmd  (car form))
             (args (cdr form)))
        (eshell-named-command cmd args)))))

Clog up our M-x

(defun ./eshell--buffer-from-dir (dir)
  "Return buffer name of an Eshell based on DIR."
  (format "*eshell: %s*"
          (thread-first dir
                        (split-string "/" t)
                        (last)
                        (car))))

(defun ./eshell-there (parent)
  "Open an eshell session in a PARENT directory.
The window is smaller and named after this directory.
If an Eshell is already present that has been named
after PARENT, pop to that buffer instead."
  (if-let* ((term-name (./eshell--buffer-from-dir parent))
            (buf-name  (seq-contains (buffer-list) term-name
                                     (lambda (a b) (string-equal (buffer-name b) a)))))
      (pop-to-buffer buf-name)

    (let* ((default-directory parent)
           (height (/ (window-total-height) 3)))
      (split-window-vertically (- height))
      (other-window 1)
      (setq eshell-buffer-name term-name)
      (eshell))))

(defun ./eshell-here ()
  "Opens a new shell in the directory of the current buffer.
Renames the eshell buffer to match that directory to allow more
than one eshell window."
  (interactive)
  (./eshell-there (if (buffer-file-name)
                    (file-name-directory (buffer-file-name))
                  default-directory)))

(bind-key "C-`" './eshell-here)

(defun ./eshell-send (command &optional dir)
  "Send COMMAND to the Eshell buffer named with DIR.
  The Eshell may have moved away from the directory originally
  opened with DIR, but it should have the name of the buffer.
  See `eshell--buffer-from-dir'."
  (interactive "sCommand to Send: ")
  (unless dir
    (setq dir (projectile-project-root)))
  (save-window-excursion
    (eshell-there dir)
    (goto-char (point-max))
    (insert command)
    (eshell-send-input)))
(defun ./execute-command-on-file-buffer (cmd)
  "Executes a shell command, CMD, on the current buffer's file.
Appends the filename to the command if not specified, so:

    chmod a+x

Works as expected. We replace the special variable `$$' with the
filename of the buffer. Note that `eshell-command' executes this
command, so eshell modifiers are available, for instance:

    mv $$ $$(:r).txt

Will rename the current file to now have a .txt extension.
See `eshell-display-modifier-help' for details on that."
  (interactive "sExecute command on File Buffer: ")
  (let* ((file-name (buffer-file-name))
         (full-cmd (cond ((string-match (rx "$$") cmd)
                          (replace-regexp-in-string (rx "$$") file-name cmd))
                         ((and file-name (string-match (rx (literal file-name)) cmd))
                          cmd)
                         (t
                          (concat cmd " " file-name)))))
    (message "Executing: %s" full-cmd)
    (eshell-command full-cmd)))

Use package - eshell settings

(use-package eshell
  :elpaca nil
  :init
  (setq eshell-error-if-no-glob t
        ;; This jumps back to the prompt:
        eshell-scroll-to-bottom-on-input 'all
        eshell-hist-ignoredups t
        eshell-save-history-on-exit t

        ;; Since eshell starts fast, let's dismiss it on exit:
        eshell-kill-on-exit t
        eshell-destroy-buffer-when-process-dies t

        ;; Parameter differences could be hard to remember. Maybe next time
        eshell-prefer-lisp-functions nil))

EAT

(use-package eat
  :config
  (define-key eat-mode-map (kbd "C-c C-d") #'eat-self-input)
  ;; :hook
  ;; (eshell-mode . #'eat-eshell-mode)
  )
  (provide 'packages)