Literate Emacs Config

About

This web page is a tangled version of my literate elisp config. Throughout this document, you may find helpful notes, tricks, and comments. I have been tinkering with emacs in some way since 2013. I made this repeatable and git-controlled in 2019, and in 2023 I began to publish it on my web site.

Notes on code blocks

If a code block is active, then it will appear with emacs-lisp syntax highlighting. If it’s not being used, then it will show up with plaintext highlighting. For example:

(print "This code will not be run!")
(print "This code is run in my config!")

Whether a code block is run at load time is controlled by the :load yes block argument of literate-elisp.

Package Management

I love and rely heavily on straight.el.

(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))

(setq straight-use-package-by-default t)
(straight-use-package 'use-package)
(require 'use-package)
(setq use-package-compute-statistics t)

(use-package general)
(use-package org)

(use-package
  literate-elisp
  :demand t
  :config
  ;; ;; To make \`elisp-refs' work with \`literate-elisp', we need to add an advice to \`elisp-refs&#x2013;read-all-buffer-forms'.
  (eval-after-load "elisp-refs"
    '(advice-add
      'elisp-refs&#x2013;read-all-buffer-forms
      :around #'literate-elisp-refs&#x2013;read-all-buffer-forms))
  ;; To make \`elisp-refs' work with \`literate-elisp', we need to add an advice to \`elisp-refs&#x2013;loaded-paths'.
  (eval-after-load "elisp-refs"
    '(advice-add
      'elisp-refs&#x2013;loaded-paths
      :filter-return #'literate-elisp-refs&#x2013;loaded-paths))
  ;; To make \`helpful' work with \`literate-elisp', we need to add an advice to \`helpful&#x2013;find-by-macroexpanding'.
  (eval-after-load 'helpful
    '(advice-add
      'helpful&#x2013;find-by-macroexpanding
      :around #'literate-elisp-helpful&#x2013;find-by-macroexpanding)))

(require 'org-element) ; TODO: until org-element&#x2013;cache-active-p autoload fixed in literate-elisp upstream
(provide 'packages-settings)

MacOS specific settings

(when (eq system-type 'darwin)
  (setq mac-command-modifier 'control)
  (setq mac-right-command-modifier 'meta)

  (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))

  (add-to-list
   'default-frame-alist
   '(ns-appearance . light)) ;; or dark - depending on your theme

  (use-package osx-plist :straight '(osx-plist :type git :host github :repo "gonewest818/osx-plist")))

Path

(setq default-directory "~/")
(use-package package)
(use-package
 exec-path-from-shell
 :demand t
 :init
 (setq exec-path-from-shell-variables
       '("PATH" "MANPATH" "OPENAI_API_KEY"))
 :config
 (when (memq window-system '(mac ns x))
   (exec-path-from-shell-initialize)))

Window management

Zoom

I think this is a little too aggressive right now, but it’s a cool idea.

(use-package
 zoom
 :init (setq zoom-size '(0.618 . 0.618))
 :config (zoom-mode))

Visual Fill Column

(use-package
 visual-fill-column
 :init (setq visual-fill-column-center-text t)
 :config (visual-fill-column-mode 1))

Centered window

(use-package
 centered-window
 :init (setq cwm-centered-window-width 180)
 :ensure t
 :config (centered-window-mode t)
 ; reload the fringe color after loading the theme
 (cwm-update-fringe-background))

Miscellaneous

(setq require-final-newline t)
(setq show-trailing-whitespace t)
(setq native-comp-async-report-warnings-errors nil)
;; Show the line number of the cursor in the mode bar at the bottom of each buffer
(setq line-number-mode t)
;; Disable the loud bell
(setq ring-bell-function
      (lambda ()
        (let ((orig-fg (face-foreground 'mode-line)))
          (set-face-foreground 'mode-line "#F2804F")
          (run-with-idle-timer 0.1 nil
                               (lambda (fg)
                                 (set-face-foreground 'mode-line fg))
                               orig-fg))))

;; Make sure all backup files only live in one place
(setq backup-directory-alist `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))
;; Don't truncate lines
(setq truncate-lines t)
(setq-default indent-tabs-mode nil)

;; Don't show the scroll bar on the side of buffers
(scroll-bar-mode -1)
;; Don't show the toolbar, it just takes up space
(tool-bar-mode -1)

;; Show column number in the modeline
(setq column-number-mode t)
(setq blink-paren-function nil)
(setq inhibit-startup-screen t)

(use-package
 ctrlf
 :config
 (add-to-list
  'ctrlf-minibuffer-bindings '("C-r" . ctrlf-backward-default))
 (setq ctrlf-default-search-style 'fuzzy-regexp)
 (setq ctrlf-default-search-style 'literal)
 (ctrlf-mode t))

(use-package
 whole-line-or-region
 :config (whole-line-or-region-global-mode t))

(use-package
 popper
 :bind
 (("C-`" . popper-toggle-latest)
  ("M-`" . popper-cycle)
  ("C-M-`" . popper-toggle-type))
 :init
 (setq popper-reference-buffers
       '("\\*Messages\\*"
         "Output\\*$"
         "\\*Async Shell Command\\*"
         help-mode
         compilation-mode))
 (popper-mode +1) (popper-echo-mode +1))

(use-package page-break-lines :config (global-page-break-lines-mode))

(use-package eldoc :hook (prog-mode . eldoc-mode))

(use-package
 expand-region
 :config (global-set-key (kbd "M-J") 'er/expand-region))

(use-package which-key :config (which-key-mode 1))

(use-package
 ibuffer
 :config
 (global-set-key (kbd "C-x C-b") 'ibuffer)
 (define-key ibuffer-mode-map (kbd "M-o") nil))

Direnv

(use-package
 direnv
 :config (direnv-mode 't)
)

Window movement keybindings

(define-key term-raw-map (kbd "M-o") 'next-multiframe-window)
(define-key term-raw-map (kbd "M-i") 'previous-multiframe-window)
(define-key global-map (kbd "M-o") 'next-multiframe-window)
(define-key global-map (kbd "M-i") 'previous-multiframe-window)

(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))

Sqlite

So far, I am unable to find a way to compile sqlite3 on my own, but I would love to be able to do this with straight package management.

;; Override the 'yes-or-no-p' temporarily
(let ((original-yes-or-no-p (symbol-function 'yes-or-no-p)))
  (fset 'yes-or-no-p (lambda (&rest args) t))

  ;; Load the sqlite3 package
  (use-package sqlite3
    :straight (sqlite3
               :type git
               :host github
               :repo "pekingduck/emacs-sqlite3-api"
               :files ("*.c" "*.h" "*.el" "Makefile")))

  ;; Restore the original function
  (fset 'yes-or-no-p original-yes-or-no-p))

Anzu

(use-package
  anzu

  :bind
  (([remap query-replace] . #'anzu-query-replace)
   ([remap query-replace-regexp] . #'anzu-query-replace-regexp))
  :config (global-anzu-mode +1))

Line Highlighting

(use-package lin :init (lin-global-mode t))

(use-package
 hl-line
 :config
 (add-hook
  'eshell-mode-hook (lambda () (setq-local global-hl-line-mode nil)))
 (add-hook
  'term-mode-hook (lambda () (setq-local global-hl-line-mode nil)))
 (add-hook
  'vterm-mode-hook (lambda () (setq-local global-hl-line-mode nil)))
 (global-hl-line-mode t))

(literate-elisp-load "term-settings.org")

(use-package dash)
(use-package ht)

;; Replace the text of selections
(pending-delete-mode t)

Undo Tree

I haven’t found a great way to use undo-tree, but everyone seems to love it.

(use-package undo-tree :init (global-undo-tree-mode))

So long mode

I find this breaks browsing of long json files, more than it helps me out.

(global-so-long-mode t)

Dashboard

(use-package
 dashboard
 :straight
 '(emacs-dashboard
   :type git
   :host github
   :repo "emacs-dashboard/emacs-dashboard"
   :files ("banners" :defaults))
 :config
 ;; Set the title
 (setq dashboard-banner-logo-title "Welcome to Emacs!")
 ;; Set the banner
 (setq dashboard-startup-banner 'official)
 (setq dashboard-items
       '((projects . 5)
         (recents . 5) (bookmarks . 5)
         ;; (agenda . 5)
         (registers . 5)))
 ;; ;; Value can be
 ;; ;; 'official which displays the official emacs logo
 ;; ;; 'logo which displays an alternative emacs logo
 ;; ;; 1, 2 or 3 which displays one of the text banners
 ;; ;; "path/to/your/image.png" which displays whatever image you would prefer

 ;; ;; Content is not centered by default. To center, set
 (setq dashboard-center-content t)
 (setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))

 ;; ;; To disable shortcut "jump" indicators for each section, set
 ;; (setq dashboard-show-shortcuts nil)

 ;; Override this function so that we can filter remote projects
 (defun dashboard-projects-backend-load-projects ()
   "Depending on `dashboard-projects-backend' load corresponding backend.
  Return function that returns a list of projects."
   (cl-remove-if
    (lambda (x) (string-search "/ssh" x))
    (cl-case
     dashboard-projects-backend
     (`projectile
      (require 'projectile)
      (dashboard-mute-apply (projectile-cleanup-known-projects))
      (projectile-load-known-projects))
     (`project-el
      (require 'project)
      (dashboard-mute-apply
       (dashboard-funcall-fboundp #'project-forget-zombie-projects))
      (project-known-project-roots))
     (t
      (display-warning
       '(dashboard) "Invalid value for `dashboard-projects-backend'"
       :error)))))

 (dashboard-setup-startup-hook))

Icons

(use-package all-the-icons)
(use-package
 all-the-icons-ibuffer
 :hook (ibuffer-mode . all-the-icons-ibuffer-mode))

;; I don't think I like buffer expose after all
(use-package
 all-the-icons-completion

 :config (all-the-icons-completion-mode)
 :hook (marginalia-mode . all-the-icons-completion-marginalia-setup))

(literate-elisp-load "projectile-settings.org")

; Re-enable with SVG support
(use-package
 svg-lib
 :straight '(svg-lib :host github :repo "emacs-straight/svg-lib"))
(use-package
 kind-icon
 :straight '(kind-icon :host github :repo "jdtsmith/kind-icon")
 :ensure t
 :after corfu
 :config
 ; On my linux machine, I need to use smaller icons like this due to the 4k display
 (when (eq system-type 'gnu/linux)
   (setq kind-icon-default-style
         '(:padding
           -1
           :stroke 0
           :margin 0
           :radius 0
           :height 0.5
           :scale 1.0)))
 :custom
 (kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
 :config (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

Fonts

;; Font settings
(if (eq system-type 'darwin)
    (if (and (display-graphic-p) (> (x-display-pixel-width) 1440))
        ;; Set default font larger if on a big screen
        (set-face-font 'default "roboto mono-15")
      ;; (set-face-font 'default "arial-15")

      ;; else
      (set-face-font 'default "roboto mono-14")
      ;; (set-fontset-font "fontset-default" "Menlo 12")
      )
  ;; else
  (if (not (eq window-system nil))
      (if (and (display-graphic-p) (> (x-display-pixel-width) 1440))
          ;; Set default font larger if on a big screen
          (set-face-font 'default "roboto mono-12")
        ;; else
        (set-face-font 'default "roboto mono-12")
        ;; (set-fontset-font "fontset-default" "Menlo 12")
        )
    ;; else
    ))

Ace popup

;; Use ace-popup-menu for completions
(use-package
 ace-popup-menu
 :config
 (ace-popup-menu-mode 1)
 (setq ace-popup-menu-show-pane-header t))

Emacs startup profiler

(use-package esup)

Scratch

(use-package
 scratch
 :defer t
 :straight
 '(scratch
   :host nil
   :type git
   :repo "https://codeberg.org/emacs-weirdware/scratch.git")
 :config (scratch--create 'emacs-lisp-mode "*scratch*"))

(use-package fuzzy)
(use-package fuzzy-match)

(use-package free-keys)
(use-package restart-emacs)

; ---- Auto Revert Modes ----- ;
(autoload 'eimp-mode "eimp" "Emacs Image Manipulation Package." t)
(add-hook 'image-mode-hook 'auto-revert-mode)

; --- CSV --- ;
(use-package
 csv-mode
 :straight
 '(csv-mode :type git :host github :repo "emacsmirror/csv-mode"))

(use-package
 explain-pause-mode
 :straight
 '(explain-pause-mode
   :type git
   :host github
   :repo "lastquestion/explain-pause-mode"))

;; use helpful instead of the normal help buffers
;; Note that the built-in `describe-function' includes both functions
;; and macros. `helpful-function' is functions only, so we provide
;; `helpful-callable' as a drop-in replacement.
(use-package
 helpful
 :defer t
 ;; TODO: Add these back in when helpful plays nicely with literate-elisp
 ;; :bind
 ;; ("C-h f" . helpful-callable)
 ;; ("C-h v" . helpful-variable)
 ;; ("C-h k" . helpful-key)
 )

(use-package
 dimmer
 :config
 (dimmer-configure-which-key)
 (dimmer-configure-org)
 (dimmer-configure-posframe)
 (dimmer-configure-magit)
 (dimmer-configure-hydra)

 (setq dimmer-fraction 0.15)
 (dimmer-mode t))

(use-package
 volatile-highlights

 :config (volatile-highlights-mode t))

(use-package hl-todo :init (global-hl-todo-mode))

Indentation

(electric-indent-mode 0)
;; (use-package
;;  aggressive-indent
;;  :config (aggressive-indent-global-mode nil))

Autoformatting

(use-package
 apheleia
 :config
 (setf (alist-get 'isort apheleia-formatters)
       '("isort" "--stdout" "-"))
 (setf (alist-get 'python-ts-mode apheleia-mode-alist) '(isort black))
 (add-to-list
  'apheleia-formatters
  '(prettier-toml
    npx "prettier" "--stdin-filepath" filepath "--parser=toml"))
 (add-to-list 'apheleia-mode-alist '(conf-toml-mode . prettier-toml))
 (defun apheleia-indent-region+ (orig scratch callback)
   (with-current-buffer scratch
     (setq-local indent-line-function
                 (buffer-local-value 'indent-line-function orig))
     (indent-region (point-min) (point-max))
     (funcall callback scratch)))

 (push '(jsonian-mode . prettier-json) apheleia-mode-alist)
 (setq apheleia-mode-alist
       (assq-delete-all 'emacs-lisp-mode apheleia-mode-alist))
 (apheleia-global-mode t))

(literate-elisp-load "elisp-settings.org")
(use-package
 elisp-autofmt
 :commands (elisp-autofmt-mode elisp-autofmt-buffer)
 :hook (emacs-lisp-mode . elisp-autofmt-mode)
 :init (setq elisp-autofmt-check-elisp-autofmt-exists 'always)
 :straight
 '(elisp-autofmt
   ;; :files (:defaults "elisp-autofmt")
   :host nil
   :type git
   :repo "https://codeberg.org/ideasman42/emacs-elisp-autofmt.git")
 :config (setq elisp-autofmt-on-save-p nil))

Whitespace butler

(use-package ws-butler
:hook (prog-mode . ws-butler-mode)
:hook (org-mode . ws-butler-mode))

Global Keybindings

(global-set-key (kbd "M-k") 'kill-this-buffer)
(global-set-key (kbd "C-c C-b") 'compile)
(global-set-key (kbd "M-0") 'delete-window)
(global-set-key (kbd "M-1") 'delete-other-windows)
(global-set-key (kbd "M-2") 'split-window-below)
(global-set-key (kbd "M-3") 'split-window-right)

; Unbind reverse search because we'll use swiper
(global-unset-key (kbd "C-r"))

(global-set-key (kbd "M-u") 'upcase-dwim)
(global-set-key (kbd "M-l") 'downcase-dwim)

(global-set-key (kbd "C-.") 'xref-find-definitions-other-window)
(define-key global-map (kbd "RET") 'newline-and-indent)

Copying syntax highlighting to the clipboard

(when (eq system-type 'darwin)
  (use-package
   highlight2clipboard
   :straight
   '(highlight2clipboard
     :type git
     :host github
     :repo "Lindydancer/highlight2clipboard")))

Breadcrumb mode

Because I’m trying this out, I’m going to disable LSP’s breadcrumb mode, which I’ve been disappointed with.

(use-package
 breadcrumb
 :straight '(breadcrumb :type git :host github :repo "joaotavora/breadcrumb")
 :config (breadcrumb-mode t))

Rsync-mode

Loving use of my old colleague Ryan Pilgrim’s package to sync accross our secure environments.

(use-package
 rsync-mode
 :straight '(rsync-mode :type git :host github :repo "jsigman/rsync-mode"))

Shortcuts

Jump to remote

(defun open-remote-dired ()
  "Opens a Dired buffer at the path specified by REMOTE_HOST and REMOTE_PATH environment variables."
  (interactive)
  (condition-case nil
      (let ((remote-host (getenv "REMOTE_HOST"))
            (remote-path (getenv "REMOTE_PATH")))
        (if (and remote-host remote-path)
            (progn
              (message "Attempting to open remote directory...")
              (dired (concat "/ssh:" remote-host ":" remote-path))
              (message "Remote directory opened."))
          (message
           "Error: REMOTE_HOST or REMOTE_PATH environment variables not set.")))
    (error
     (message
      "Error: Unable to open remote directory. Check your connection and environment variables."))))

;; Bind the function to M-R globally
(global-set-key (kbd "M-R") 'open-remote-dired)

Local Variables Settings

This really improves use with local variables. You get a highly visible warning when a dir-local file is unreadable or misconfigured.

(defun my/dir-local-error-warning (orig-fun &rest args)
  "Advice to display a warning on directory local variable read errors."
  (condition-case err
      (apply orig-fun args)
    (error
     (display-warning
      'dir-locals
      (format "Error reading .dir-locals.el: %s"
              (error-message-string err))
      :error))))

(advice-add
 'hack-dir-local-variables
 :around #'my/dir-local-error-warning)

Config Modes

Yaml

(use-package yaml-mode)

Parens

(setq show-paren-when-point-inside-paren 't)
(setq show-paren-style 'mixed)
(setq show-paren-context-when-offscreen 't)

(use-package
 elec-pair
 :config ;; Disable electric pair in minibuffer
 (defun my/inhibit-electric-pair-mode (char)
   (or (minibufferp) (electric-pair-conservative-inhibit char)))
 (setq electric-pair-inhibit-predicate
       #'my/inhibit-electric-pair-mode)

 (electric-pair-mode t)
 ;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition.
 (setq electric-pair-inhibit-predicate
       (lambda (c)
         (or (member c '(?< ?> ?~))
             (electric-pair-default-inhibit c)))))

(setq show-paren-context-when-offscreen t)
(setq show-paren-style 'mixed)


;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’.
(modify-syntax-entry ?< "w<")
(modify-syntax-entry ?> "w>")

;; Show matching parens
(setq show-paren-delay 0)
(show-paren-mode t)

Elisp

(use-package eros :config (eros-mode t))
(use-package
 lisp-extra-font-lock
 :config (lisp-extra-font-lock-global-mode 1))

(use-package elisp-docstring-mode)
(use-package
 highlight-function-calls
 :hook (emacs-lisp-mode . highlight-function-calls-mode))

(use-package format-all :hook (elisp-mode . format-all-mode))
(use-package
 inspector
 :straight
 '(inspector :type git :host github :repo "mmontone/emacs-inspector"))

(setq eval-expression-print-length nil)
(setq eval-expression-print-level nil)

(add-to-list
 'auto-mode-alist
 '("\\.dir-locals\\(?:-2\\)?\\.el\\'" . emacs-lisp-mode))

Dired Mode

(setq
 dired-omit-files
 "^\\.?#\\|^\\.\\(DS_Store\\|localized\\|AppleDouble\\)$\\|^\\.\\.$")
(setq dired-kill-when-opening-new-dired-buffer t)
(when (eq system-type 'darwin) (setq
 insert-directory-program "gls"
 dired-use-ls-dired t))
(setq dired-listing-switches "-al --group-directories-first")

;; wdired settings
(use-package
 wdired
 :config
 (setq wdired-allow-to-change-permissions t)
 (define-key dired-mode-map (kbd "e") 'wdired-change-to-wdired-mode)
 (define-key dired-mode-map (kbd "M-G") nil))

Projectile

(use-package
 projectile
 :init (setq projectile-git-submodule-command nil)
 ;; always ignore the home directory and root
 (setq projectile-ignored-projects
       `("/" "~/" ,(expand-file-name "~/")))

 (setq projectile-track-known-projects-automatically nil)

 ;; Use alien as the default, and project-wise add other files
 (setq projectile-indexing-method 'native)
 (setq projectile-enable-caching t)
 (setq projectile-files-cache-expire 300)
 (setq projectile-file-exists-remote-cache-expire nil)

 :config
 (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
 (define-key
  projectile-mode-map (kbd "C-c p") 'projectile-command-map)
 (define-key projectile-mode-map (kbd "M-K") 'projectile-kill-buffers)

 (add-to-list 'projectile-globally-ignored-directories "/venv")
 (add-to-list 'projectile-globally-ignored-directories "/data")
 (add-to-list 'projectile-globally-ignored-directories "/typings")
 (add-to-list 'projectile-globally-ignored-directories "/node_modules")
 (add-to-list 'projectile-globally-ignored-directories "/.mypy_cache")
 (add-to-list
  'projectile-globally-ignored-directories "/.pytest_cache")
 (add-to-list 'projectile-globally-ignored-directories "/.cache")
 (add-to-list 'projectile-globally-ignored-directories "/.dvc/cache")
 (add-to-list 'projectile-globally-ignored-directories "/.dvc/tmp")
 (add-to-list
  'projectile-globally-ignored-directories "/.jekyll-cache")
 (projectile-mode +1)
 ;; (add-hook 'magit-run-section-hook 'projectile-invalidate-cache)
 (add-hook
  'magit-section-post-command-hook 'projectile-invalidate-cache)
)

Corfu-Vertico-Orderless

Corfu

(use-package
 corfu
 ;; Optional customizations
 :custom
 (corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
 (corfu-auto t) ;; Enable auto completion
 ;; (corfu-commit-predicate nil)   ;; Do not commit selected candidates on next input
 (corfu-quit-at-boundary 'separator) ;; Automatically quit at word boundary
 (corfu-quit-no-match 'separator) ;; Automatically quit if there is no match
 (corfu-scroll-margin 5) ;; Use scroll margin
 ;; (corfu-preview-current nil)    ;; Do not preview current candidate
 (corfu-auto-delay 0.0)
 (corfu-auto-prefix 1)
 (corfu-on-exact-match 'quit)

 ;; (corfu-separator ?\s)          ;; Orderless field separator
 ;; (corfu-preview-current nil)    ;; Disable current candidate preview
 ;; (corfu-preselect-first nil)    ;; Disable candidate preselection
 ;; (corfu-on-exact-match nil)     ;; Configure handling of exact matches
 ;; (corfu-echo-documentation nil) ;; Disable documentation in the echo area
 ;; (corfu-scroll-margin 5)        ;; Use scroll margin

 ;; You may want to enable Corfu only for certain modes.
 ;; :hook ((prog-mode . corfu-mode)
 ;;        (shell-mode . corfu-mode)
 ;;        (eshell-mode . corfu-mode))

 ;; Recommended: Enable Corfu globally.
 ;; This is recommended since dabbrev can be used globally (M-/).
 :init (global-corfu-mode)

 ;; :config
 ;; (define-key corfu-map (kbd "M-p") #'corfu-doc-scroll-down) ;; corfu-next
 ;; (define-key corfu-map (kbd "M-n") #'corfu-doc-scroll-up)  ;; corfu-previous

 ;; Quit on save
 :hook (before-save-hook . corfu-quit)
 :load-path "straight/build/corfu/extensions"
 :config
 (require 'corfu-history)
 (corfu-history-mode 1)
 (savehist-mode 1)
 (add-to-list 'savehist-additional-variables 'corfu-history)
 ;; (corfu-mode-hook . corfu-doc-mode)
 )

Cape

(defun add-cape-completions ()
  (add-to-list 'completion-at-point-functions #'cape-file)
  ;; (add-to-list 'completion-at-point-functions
  ;;              #'cape-keyword)
  ;; (add-to-list 'completion-at-point-functions
  ;;              #'cape-symbol)
  )

;; Add extensions
(use-package
 cape
 ;; Bind dedicated completion commands
 ;; :bind (("C-c p p" . completion-at-point) ;; capf
 ;;        ("C-c p t" . complete-tag)        ;; etags
 ;;        ("C-c p d" . cape-dabbrev)        ;; or dabbrev-completion
 ;;        ("C-c p f" . cape-file)
 ;;        ("C-c p k" . cape-keyword)
 ;;        ("C-c p s" . cape-symbol)
 ;;        ("C-c p a" . cape-abbrev)
 ;;        ("C-c p i" . cape-ispell)
 ;;        ("C-c p l" . cape-line)
 ;;        ("C-c p w" . cape-dict)
 ;;        ("C-c p \\" . cape-tex)
 ;;        ("C-c p _" . cape-tex)
 ;;        ("C-c p ^" . cape-tex)
 ;;        ("C-c p &" . cape-sgml)
 ;;        ("C-c p r" . cape-rfc1345))
 :hook (corfu-mode . add-cape-completions))
;; A few more useful configurations...
(setq completion-cycle-threshold 3)

Orderless

;; Optionally use the `orderless' completion style.
(use-package
 orderless
 :init
 ;; Tune the global completion style settings to your liking!
 ;; This affects the minibuffer and non-lsp completion at point.
 (setq
  completion-styles '(orderless partial-completion basic)
  completion-category-defaults nil
  completion-category-overrides nil))

;; ;; Use dabbrev with Corfu!
;; (use-package dabbrev
;;   ;; Swap M-/ and C-M-/
;;   :bind (("M-/" . dabbrev-completion)
;;          ("C-M-/" . dabbrev-expand)))

;; A few more useful configurations...
(use-package
 emacs
 :init
 ;; 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)
 )

;; Enable vertico
(use-package
 vertico
 :init (vertico-mode)
 :bind (:map vertico-map ("C-j" . vertico-exit-input))

 ;; 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)
 )

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

;; A few more useful configurations...
(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))

Selectrum

Currently using Vertico over this

(setq projectile-completion-system 'default)
;; (setq projectile-completion-system 'default)
(use-package selectrum :config (selectrum-mode t))

(use-package
 selectrum-prescient
 :config (selectrum-prescient-mode t) (prescient-persist-mode t)


 (setq enable-recursive-minibuffers t)

 ;; Persist history over Emacs restarts
 (savehist-mode)

 ;; Optional performance optimization
 ;; by highlighting only the visible candidates.
 (setq orderless-skip-highlighting (lambda () selectrum-is-active))
 (setq selectrum-highlight-candidates-function
       #'orderless-highlight-matches))

Ivy/Counsel/Swiper Settings

Currently not using this in favor of Vertico

(use-package ivy)
(use-package counsel)
(use-package swiper)

;; Case insensitive searchs
(setq completion-ignore-case t)
(setq case-fold-search nil)

(setq
 counsel-find-file-ignore-regexp
 "^\\.?#\\|^\\.\\(DS_Store\\|localized\\|AppleDouble\\)$\\|^\\.\\.$")

(ivy-mode 1) ;; Turn on ivy by default
(setq ivy-use-virtual-buffers t) ;; no idea, but recommended by project maintainer
(setq enable-recursive-minibuffers t) ;; no idea, but recommended by project maintainer
(setq ivy-count-format "(%d/%d) ") ;; changes the format of the number of results


(global-set-key
 (kbd "C-s")
 'swiper) ;; replaces i-search with swiper
(global-set-key
 (kbd "M-x")
 'counsel-M-x) ;; Gives M-x command counsel features
(global-set-key
 (kbd "C-x C-f")
 'counsel-find-file) ;; gives C-x C-f counsel features

(global-set-key
 (kbd "C-x b")
 'counsel-switch-buffer) ;; gives C-x C-f counsel features
(global-set-key (kbd "<f1> f") 'counsel-describe-function)
(global-set-key (kbd "<f1> v") 'counsel-describe-variable)
(global-set-key (kbd "<f1> l") 'counsel-find-library)
(global-set-key (kbd "<f2> i") 'counsel-info-lookup-symbol)
(global-set-key (kbd "<f2> u") 'counsel-unicode-char)
(global-set-key (kbd "C-c C-r") 'ivy-resume)

(global-set-key (kbd "C-c g") 'counsel-git)
(global-set-key (kbd "C-c j") 'counsel-git-grep)
(global-set-key
 (kbd "C-c k")
 'counsel-ag) ;; add counsel/ivy features to ag package
(global-set-key (kbd "C-x l") 'counsel-locate)
(global-set-key (kbd "C-S-o") 'counsel-rhythmbox)
;;(define-key read-expression-map (kbd "C-r") 'counsel-expression-history)

(define-key ivy-minibuffer-map (kbd "C-j") 'ivy-immediate-done)
(define-key ivy-minibuffer-map (kbd "C-M-j") nil)

;;set action options during execution of counsel-find-file
;; replace "frame" with window to open in new window
(ivy-set-actions
 'counsel-find-file
 '(("j" find-file-other-frame "other frame")
   ("b" counsel-find-file-cd-bookmark-action "cd bookmark")
   ("x" counsel-find-file-extern "open externally")
   ("d" delete-file "delete")
   ("r" counsel-find-file-as-root "open as root")))

;; set actions when running C-x b
;; replace "frame" with window to open in new window
(ivy-set-actions
 'ivy-switch-buffer
 '(("j" switch-to-buffer-other-frame "other frame")
   ("k" kill-buffer "kill")
   ("r" ivy--rename-buffer-action "rename")))

;; Use fuzzy matching for ivy in modes that aren't swiper
(setq ivy-re-builders-alist
      '((swiper . ivy--regex-plus) (t . ivy--regex-fuzzy)))

;; Don't list .. and . in the ivy files search
(setq ivy-extra-directories ())

;; Persist histories across startups
(use-package prescient)
(use-package ivy-prescient)
;; Use prescient to store history of search results and preference based on these
(ivy-prescient-mode)

(prescient-persist-mode t)

(use-package counsel-projectile)
(counsel-projectile-mode)
(setq projectile-completion-system 'ivy)

(use-package
 all-the-icons-ivy-rich
 :config (all-the-icons-ivy-rich-mode t))
(use-package ivy-rich :config (ivy-rich-mode t))

Company Settings

Company was a great package, but I’m currently not using it. I migrated over to the simpler and more flexible Corfu.

(use-package
 company
 :config
 ;; Dont wait to show completions
 (setq company-idle-delay 0)
 (setq company-minimum-prefix-length 1)
 (setq company-quickhelp-delay nil)
 (global-company-mode t)
 (use-package
  company-async-files
  :straight
  '(company-async-files
    :type git
    :host github
    :repo "CeleritasCelery/company-async-files"))

 ; I dont want these company backends
 (setq company-backends (remove 'company-bbdb company-backends))
 (setq company-backends (remove 'company-eclim company-backends))
 (setq company-backends (remove 'company-semantic company-backends))
 (setq company-backends (remove 'company-clang company-backends))
 (setq company-backends (remove 'company-xcode company-backends))
 (setq company-backends (remove 'company-cmake company-backends))
 ;; (setq company-backends (remove 'company-files company-backends))
 (setq company-backends
       (remove 'company-dabbrev-code company-backends))
 (setq company-backends (remove 'company-dabbrev company-backends))
 ; I dont want these company backends
 (setq company-backends (remove 'company-bbdb company-backends))
 (setq company-backends (remove 'company-eclim company-backends))
 (setq company-backends (remove 'company-semantic company-backends))
 (setq company-backends (remove 'company-clang company-backends))
 (setq company-backends (remove 'company-xcode company-backends))
 (setq company-backends (remove 'company-cmake company-backends))
 ;; (setq company-backends (remove 'company-files company-backends))
 (setq company-backends
       (remove 'company-dabbrev-code company-backends))
 (setq company-backends (remove 'company-dabbrev company-backends)))
(use-package
 company-quickhelp
 :config ;; Sort by recent use
 (company-quickhelp-mode))
(setq company-dabbrev-downcase nil)

(use-package company-box :hook (company-mode . company-box-mode))
(use-package company-prescient :config (company-prescient-mode t))

;; Add yasnippet support for all company backends
;; https://github.com/syl20bnr/spacemacs/pull/179
(defvar company-mode/enable-yas t
  "Enable yasnippet for all backends.")
(defun company-mode/backend-with-yas (backend)
  (if (or (not company-mode/enable-yas)
          (and (listp backend) (member 'company-yasnippet backend)))
      backend
    (append
     (if (consp backend)
         backend
       (list backend))
     '(:with company-yasnippet))))
(setq company-backends
      (mapcar #'company-mode/backend-with-yas company-backends))
(use-package company-posframe :config (company-posframe-mode t))

MATLAB

(use-package
 matlab-mode
 '(matlab-mode
   :type git
   :repo "https://git.code.sf.net/p/matlab-emacs/src"))
(require 'matlab)

(setq matlab-shell-command-switches '("-nodesktop" "-nosplash"))

Consult/Embark/Marginalia

Marginalia

(use-package marginalia :demand t :config (marginalia-mode))

Embark

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

 :init
 ;; Optionally replace the key help with a completing-read interface
 (setq prefix-help-command #'embark-prefix-help-command)
 :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
 :after consult
 :hook (embark-collect-mode . consult-preview-at-point-mode))

Consult

;; Example configuration for Consult
(use-package
 consult
 :demand t
 ;; Replace bindings. Lazily loaded due by `use-package'.
 :bind
 ( ;; C-c bindings (mode-specific-map)
  ("C-c h" . consult-history)
  ("C-c m" . consult-mode-command)
  ("C-c b" . consult-bookmark)
  ("C-c k" . consult-kmacro)
  ;; C-x bindings (ctl-x-map)
  ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
  ("C-x b" . consult-buffer) ;; orig. switch-to-buffer
  ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
  ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
  ;; Custom M-# bindings for fast register access
  ("M-#" . consult-register-load)
  ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
  ("C-M-#" . consult-register)
  ;; Other custom bindings
  ("M-y" . consult-yank-pop) ;; orig. yank-pop
  ("<help> a" . consult-apropos) ;; orig. apropos-command
  ;; M-g bindings (goto-map)
  ("M-g e" . consult-compile-error)
  ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
  ("M-g g" . consult-goto-line) ;; orig. goto-line
  ("M-g M-g" . consult-goto-line) ;; orig. goto-line
  ("M-g o" . consult-outline) ;; Alternative: consult-org-heading
  ("M-g m" . consult-mark)
  ("M-g k" . consult-global-mark)
  ("M-g i" . consult-imenu)
  ("M-g I" . consult-imenu-multi)
  ;; M-s bindings (search-map)
  ("M-s f" . consult-find)
  ("M-s F" . consult-locate)
  ("M-s g" . consult-grep)
  ("M-s G" . consult-git-grep)
  ;; ("M-s r" . consult-ripgrep)
  ("M-s r" . consult-ripgrep-project-root)
  ("M-s l" . consult-line)
  ("M-s L" . consult-line-multi)
  ("M-s m" . consult-multi-occur)
  ("M-s k" . consult-keep-lines)
  ("M-s u" . consult-focus-lines)
  ;; Isearch integration
  ("M-s e" . consult-isearch-history)
  :map
  isearch-mode-map
  ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
  ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
  ("M-s l" . consult-line) ;; needed by consult-line to detect isearch
  ("M-s L" . consult-line-multi)) ;; needed by consult-line to detect isearch

 ;; Enable automatic preview at point in the *Completions* buffer. This is
 ;; relevant when you use the default completion UI. You may want to also
 ;; enable `consult-preview-at-point-mode` in Embark Collect buffers.
 :hook (completion-list-mode . consult-preview-at-point-mode)

 ;; The :init configuration is always executed (Not lazy)
 :init
 (defun consult-ripgrep-project-root (&optional initial)
   (interactive "P")
   (let ((dir (funcall consult-project-function)))
     (consult--grep
      "Ripgrep" #'consult--ripgrep-make-builder dir initial)))

 ;; (setq consult-ripgrep-args "rg --null --line-buffered --color=never --max-columns=1000 --path-separator /\ --smart-case --no-heading --line-number .")
 (setq consult-project-function 'projectile-project-root)

 ;; Optionally configure the register formatting. This improves the register
 ;; preview for `consult-register', `consult-register-load',
 ;; `consult-register-store' and the Emacs built-ins.
 (setq
  register-preview-delay 0.1
  register-preview-function #'consult-register-format)

 ;; Optionally tweak the register preview window.
 ;; This adds thin lines, sorting and hides the mode line of the window.
 (advice-add #'register-preview :override #'consult-register-window)

 ;; Optionally tweak the register preview window.
 ;; This adds thin lines, sorting and hides the mode line of the window.
 (advice-add #'register-preview :override #'consult-register-window)

 ;; Use Consult to select xref locations with preview
 (setq
  xref-show-xrefs-function #'consult-xref
  xref-show-definitions-function #'consult-xref)

 ;; Configure other variables and modes in the :config section,
 ;; after lazily loading the package.
 :config
 ;; Optionally configure preview. The default value
 ;; is 'any, such that any key triggers the preview.
 (setq consult-preview-key nil)
 ;; For some commands and buffer sources it is useful to configure the
 ;; :preview-key on a per-command basis using the `consult-customize' macro.
 (consult-customize
  consult-theme
  :preview-key
  '(:debounce 0.2 any)
  consult-ripgrep
  consult-git-grep
  consult-grep
  consult-bookmark
  consult-recent-file
  consult-xref
  consult--source-bookmark
  consult--source-file-register
  consult--source-recent-file
  consult--source-project-recent-file
  ;; :preview-key "M-."
  :preview-key '(:debounce 0.4 any))

 ;; Optionally configure the narrowing key.
 ;; Both < and C-+ work reasonably well.
 (setq consult-narrow-key "<") ;; (kbd "C-+")

 ;; Optionally make narrowing help available in the minibuffer.
 ;; You may want to use `embark-prefix-help-command' or which-key instead.
 ;; (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)

 ;; Optionally configure a function which returns the project root directory.
 ;; There are multiple reasonable alternatives to chose from.
 ;;;; 1. project.el (project-roots)
 ;; (setq consult-project-root-function
 ;;       (lambda ()
 ;;         (when-let (project (project-current))
 ;;           (car (project-roots project)))))
 ;;;; 2. projectile.el (projectile-project-root)
 (autoload 'projectile-project-root "projectile")
 (setq consult-project-root-function #'projectile-project-root)
 ;;;; 3. vc.el (vc-root-dir)
 ;; (setq consult-project-root-function #'vc-root-dir)
 ;;;; 4. locate-dominating-file
 ;; (setq consult-project-root-function (lambda () (locate-dominating-file "." ".git")))
 )

;; Optionally add the `consult-flycheck' command.
(use-package
 consult-flycheck
 :after consult
 :bind (:map flycheck-command-map ("!" . consult-flycheck)))
;; Enable Consult-Selectrum integration.
;; This package should be installed if Selectrum is used.

Latex

(use-package
 tex
 :straight auctex
 :defer t
 :hook (LaTeX-mode . visual-line-mode)
 :hook (LaTeX-mode . flyspell-mode)
 :hook (LaTeX-mode . LaTeX-math-mode)
 :hook (LaTeX-mode . TeX-source-correlate-mode)
 :config
 (setq TeX-auto-save t)
 (setq TeX-parse-self t)
 (setq-default TeX-master nil)

 ;; (add-hook 'LaTeX-mode-hook 'company-auctex-init)
 ;; (add-hook 'LaTeX-mode-hook 'company-mode)
 (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
 (setq reftex-plug-into-AUCTeX t)
 (setq TeX-PDF-mode t))
(use-package cdlatex)

;; -------------------------/AucTex-------------------------------;;

Snippets

Yasnippet

(use-package
 yasnippet
 :demand t
 :init
 (load "yasnippet.el") ; get rid of weird invalid function issue
 )
(use-package
 yasnippet-snippets
 :demand t
 :straight
 '(yasnippet-snippets
   :type git
   :host github
   :repo "jsigman/yasnippet-snippets"))

(yas-global-mode 1)
(use-package
 consult-yasnippet
 :after consult
 :config (global-set-key (kbd "M-Y") 'consult-yasnippet))

Yasnippet-Capf

(use-package
 yasnippet-capf
 :after cape
 :init
 (setq yasnippet-capf-lookup-by 'key) ;; key or name
 :config (add-to-list 'completion-at-point-functions #'yasnippet-capf))

Terminal

Vterm

(use-package
 vterm
 :init
 (setq vterm-always-compile-module t)
 (setq vterm-timer-delay 0.01)
 :config (define-key vterm-mode-map (kbd "M-k") 'kill-this-buffer)
 ;; (define-key vterm-mode-map (kbd "M-i") 'previous-multiframe-window)
 ;; (define-key vterm-mode-map (kbd "M-o") 'next-multiframe-window)
 (define-key vterm-mode-map (kbd "M-0") 'delete-window)
 (define-key vterm-mode-map (kbd "M-1") 'delete-other-windows)
 (define-key vterm-mode-map (kbd "M-2") 'split-window-below)
 (define-key vterm-mode-map (kbd "M-3") 'split-window-right)
 (define-key vterm-mode-map (kbd "M-T") 'vterm-toggle)
 (define-key vterm-mode-map (kbd "M-R") 'vterm-toggle-cd)
 ; Unbind M-s so we can use this for other commands
 (define-key vterm-mode-map (kbd "M-s") 'nil)
 (setq vterm-toggle-scope 'dedicated)
 (setq vterm-toggle-project-root t)
 (setq vterm-toggle-cd-auto-create-buffer nil)
 (setq vterm-toggle-reset-window-configration-after-exit t)
 (setq vterm-toggle-fullscreen-p nil)
 (setq vterm-toggle-hide-method 'bury-all-vterm-buffer)


 (defun kill-buffer-and-its-windows (buffer)
   "Kill BUFFER and delete its windows.  Default is `current-buffer'.
BUFFER may be either a buffer or its name (a string)."
   (interactive (list
                 (read-buffer "Kill buffer: "
                              (current-buffer)
                              'existing)))
   (setq buffer (get-buffer buffer))
   (if (buffer-live-p buffer) ; Kill live buffer only.
       (let
           ((wins (get-buffer-window-list buffer nil t))) ; On all frames.
         (when (and (buffer-modified-p buffer)
                    (fboundp '1on1-flash-ding-minibuffer-frame))
           (1on1-flash-ding-minibuffer-frame t)) ; Defined in `oneonone.el'.
         (when
             (kill-buffer buffer) ; Only delete windows if buffer killed.
           (dolist (win wins) ; (User might keep buffer if modified.)
             (when (window-live-p win)
               ;; Ignore error, in particular,
               ;; "Attempt to delete the sole visible or iconified frame".
               (condition-case nil
                   (delete-window win)
                 (error nil))))))
     (when (interactive-p)
       (error
        "Cannot kill buffer.  Not a live buffer: `%s'" buffer))))

 (setq vterm-kill-buffer-on-exit t)
 (define-key
  vterm-mode-map (kbd "M-k")
  (lambda ()
    (interactive)
    (kill-buffer-and-its-windows (current-buffer))))
 ;; Add goto-address-mode to vterm-mode-hook
 (add-hook 'vterm-mode-hook 'goto-address-mode))

Vterm Toggle

;; Special overrides of vterm toggle functions to work better with projectile
(defun vterm-toggle--new (&optional buffer-name)
  "New vterm buffer."
  (let* ((buffer-name (or buffer-name vterm-buffer-name))
         (project-root
          (when vterm-toggle-project-root
            (ignore-errors
              (projectile-project-root))))
         (default-directory (or project-root default-directory)))
    (if vterm-toggle-fullscreen-p
        (vterm buffer-name)
      (if (eq major-mode 'vterm-mode)
          (let ((display-buffer-alist nil))
            (vterm buffer-name))
        (vterm-other-window buffer-name)))))
(defun vterm-toggle--project-root ()
  (projectile-project-root))

(use-package
 vterm-toggle
 :after vterm
 :demand t
 :bind
 (:map
  vterm-mode-map
  ("<escape>" . vterm-send-C-c)
  ("M-T" . vterm-toggle)
  ("M-R" . vterm-toggle-cd)
  ("M-n" . vterm-toggle-forward)
  ("M-p" . vterm-toggle-backward))
 (:map global-map ("M-T" . vterm-toggle) ("M-R" . vterm-toggle-cd))
 :config (setq vterm-toggle-fullscreen-p nil)
 (add-to-list
  'display-buffer-alist
  '((lambda (buffer-or-name _)
      (let ((buffer (get-buffer buffer-or-name)))
        (with-current-buffer buffer
          (or (equal major-mode 'vterm-mode)
              (string-prefix-p
               vterm-buffer-name (buffer-name buffer))))))
    (display-buffer-reuse-window display-buffer-in-direction)
    (direction . bottom)
    (dedicated . t) ;dedicated is supported in emacs27
    (reusable-frames . visible)
    (window-height . 0.3)
    (window-width . 0.3))))

Ripgrep

(use-package wgrep :config (setq wgrep-auto-save-buffer t))
(use-package rg :config (rg-enable-menu) (setq rg-executable "rg"))

Avy

(use-package
 avy
 :config
 ;; (global-set-key
 ;;  (kbd "C-;")
 ;;  'avy-goto-char-timer) ;; I use this most frequently
 (global-set-key
  (kbd "C-'")
  'avy-goto-line) ;; Consistent with ivy-avy
 (global-set-key
  (kbd "C-M-'")
  'avy-goto-end-of-line) ;; Consistent with ivy-avy

 (setq avy-case-fold-search nil) ;; case sensitive makes selection easier
 (setq avy-indent-line-overlay t))

Language Server Protocol (LSP)

LSP Mode

(setq my/lsp-package "lsp")
;------------------------LSP---------------------------;
; PATCH BELOW
(setenv "LSP_USE_PLISTS" "true")
(literate-elisp-load "corfu-vertico-settings.org")
(use-package
 lsp-mode
 :custom (lsp-completion-provider :none) ; We use corfu
 :demand t
 :init
 (setq lsp-signature-render-documentation t)
 (setq lsp-signature-auto-activate t)
 (setq lsp-signature-doc-lines 3)
 (setq lsp-prefer-flymake nil)
 (setq lsp-idle-delay 0.0)
 (setq lsp-enable-snippet t)
 (setq lsp-keymap-prefix "M-P")
 (setq lsp-log-io nil)
 (setq lsp-print-performance nil)
 (setq lsp-response-timeout 10)
 (setq lsp-enable-on-type-formatting nil)
 (setq lsp-keep-workspace-alive nil)
 (setq lsp-headerline-breadcrumb-enable-diagnostics nil)
 (setq lsp-headerline-breadcrumb-enable nil)
 (setq read-process-output-max (* 1024 1024))
 (setq lsp-semantic-highlighting-faces t)
 (setq lsp-enable-imenu nil)
 (setq lsp-enable-text-document-color t)
 (setq lsp-completion-no-cache t)
 (setq lsp-completion-filter-on-incomplete nil)
 (setq lsp-enable-file-watchers t)
 (setq lsp-auto-execute-action nil)
 (setq lsp-auto-guess-root 't)
 (setq lsp-document-sync-method nil)
 (setq lsp-completion-use-last-result nil)
 (setq lsp-completion-show-kind t)
 (setq lsp-completion-show-detail t)

 ;; (defun my/lsp-mode-setup-completion ()
 ;;   (setf (alist-get
 ;;          'styles
 ;;          (alist-get 'lsp-capf completion-category-defaults))
 ;;         '(orderless))) ;; Configure orderless

 (defun my/lsp-mode-setup-completion ()
   (setf (alist-get
          'styles
          (alist-get 'lsp-capf completion-category-defaults))
         '(orderless)))

 ;; Optionally configure the cape-capf-buster.
 (setq-local completion-at-point-functions
             (list (cape-capf-buster #'lsp-completion-at-point)))
 :hook
 (lsp-mode . lsp-enable-which-key-integration)
 (lsp-completion-mode . my/lsp-mode-setup-completion)
 ;; (lsp-mode . lsp-ui-mode)
 )

LSP UI

For right now, I don’t really like LSP UI, let’s not load this config

(use-package
 lsp-ui
 :config
 (setq lsp-ui-doc-enable nil)
 (setq lsp-ui-peek-enable nil)
 (setq lsp-ui-peek-fontify "never") ;; never, on-demand, or always
 (setq lsp-ui-peek-list-width 50)
 (setq lsp-ui-peek-peek-height 20)
 (setq lsp-ui-sideline-code-actions-prefix "")
 (setq lsp-ui-doc nil)
 ;inline right flush docs
 (setq lsp-ui-sideline-enable nil)
 (setq lsp-ui-sideline-ignore-duplicate t)
 (setq lsp-ui-sideline-show-code-actions nil)
 (setq lsp-ui-sideline-show-diagnostics nil)
 (setq lsp-ui-sideline-show-hover nil)
 (setq lsp-ui-sideline-show-symbol nil)
 (setq lsp-ui-sideline-delay 0.01)
 (add-hook
  'lsp--managed-mode-hook
  (lambda nil
    (interactive)
    (remove-hook 'post-self-insert-hook 'lsp--on-self-insert t)))

 (define-key
  lsp-ui-mode-map
  [remap xref-find-definitions]
  #'lsp-ui-peek-find-definitions)
 (define-key
  lsp-ui-mode-map
  [remap xref-find-references]
  #'lsp-ui-peek-find-references)
 (setq lsp-ui-flycheck-enable nil)
 (add-hook 'flycheck-mode-hook 'lsp-ui-sideline-mode)

 (literate-elisp-load "flycheck-settings.org")
 (require 'lsp-ui-flycheck)
 (define-key
  lsp-ui-flycheck-list-mode-map
  (kbd "<M-RET>")
  'lsp-ui-flycheck-list--visit)
 (define-key
  lsp-ui-flycheck-list-mode-map
  (kbd "RET")
  'lsp-ui-flycheck-list--view)


 (setq lsp-ui-doc-header t)
 (setq lsp-ui-doc-include-signature t)
 (setq lsp-ui-doc-max-height 30)
 (setq lsp-ui-doc-max-width 120)
 (setq lsp-ui-doc-position (quote at-point))
 (setq lsp-ui-doc-use-childframe nil)

 (setq lsp-ui-doc-use-webkit nil)

 :bind
 (:map
  lsp-ui-imenu-mode-map
  ("<return" . lsp-ui-imenu--view)
  ("RET" . lsp-ui-imenu--view)))

LSP with iedit

I’m now using iedit instead of my hand-crafted lsp-mc mode. It is integrated and works slightly better.

;---Multiple cursors------;
(literate-elisp-load "multiple-cursors-settings.org")

(use-package iedit :init (setq iedit-toggle-key-default (kbd "C-,")))
(defun my/lsp-iedit ()
  (lsp-iedit-highlights)
  ;; (iedit-switch-to-mc-mode)
  )

(setq my/lsp-highlight-setup "iedit")
(pcase my/lsp-highlight-setup
  ("iedit" (use-package
    iedit
    :init
    (setq iedit-toggle-key-default nil)
    (setq iedit-auto-narrow t)
    :bind
    (:map
     lsp-mode-map
     ("M-S" . lsp-iedit-highlights)
     (:map iedit-mode-keymap ("C-g" . iedit--quit)))
    :after (:all lsp-mode)
    :custom-face (iedit-occurrence ((t (:background "Red"))))))
  ("lsp-mc" (straight-use-package
    '(lsp-mc :type git :host github :repo "jsigman/lsp-mc"))
   (require 'lsp-mc) (add-hook 'lsp-mode-hook 'lsp-mc-mode)))

Modes for LSP

Configure here the modes for which you’d like to run LSP mode.

(add-hook 'LaTeX-mode-hook #'lsp-deferred)
(add-hook 'yaml-base-mode-hook #'lsp-deferred)
(add-hook 'json-mode-hook #'lsp-deferred)
(add-hook 'dockerfile-mode-hook #'lsp-deferred)

(setq lsp-enable-on-type-formatting t)
(setq lsp-enable-indentation t)

(add-hook 'sh-mode-hook #'lsp-deferred)

(use-package lsp-mssql  :hook (sql-mode . lsp))
;; (use-package
;;  lsp-markdown 
;;  :config (add-hook 'markdown-mode-hook 'lsp-markdown))

;; ;; Automatically install lsp servers
(defun my/install-lsp-servers ()
  (interactive)
  (lsp-install-server t 'pyright)
  (lsp-install-server t 'xmlls)
  (lsp-install-server t 'yamlls)
  (lsp-install-server t 'html-ls)
  (lsp-install-server t 'json-ls)
  (lsp-install-server t 'dockerfile-ls)
  (lsp-install-server t 'bash-ls))

LSP Docker

This mode is used to run language servers in a docker container. At some point I’d like to use this.

(use-package lsp-docker)

LSP File Watchers

(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\venv\\'")
(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\.dvc\\'")
(add-to-list
 'lsp-file-watch-ignored-directories "[/\\\\]\\kubernetes\\'")
(add-to-list 'lsp-file-watch-ignored-directories "[/\\\\]\\notes\\'")
(add-to-list
 'lsp-file-watch-ignored-directories "[/\\\\]\\checkpoints\\'")
(add-to-list
 'lsp-file-watch-ignored-directories "[/\\\\]\\experiments\\'")
(add-to-list
 'lsp-file-watch-ignored-directories "[/\\\\]\\.mypy_cache\\'")
(add-to-list
 'lsp-file-watch-ignored-directories "[/\\\\]\\.pytest_cache\\'")

Eglot

Currently not using this in favor of LSP-mode

(use-package eglot)
(setq my/lsp-package 'eglot)
;; (add-to-list 'eglot-server-programs
;;              `(python-base-mode . ("pyls" "-v" "--tcp" "--host"
;;                               "localhost" "--port" :autoport)))
(add-hook 'python-base-mode-hook 'eglot-ensure)
(add-hook 'shell-script-mode 'eglot-ensure)
(add-hook 'yaml-mode 'eglot-ensure)
(add-hook 'js-mode 'eglot-ensure)

; Disable flymake in eglot buffers
(add-hook 'eglot--managed-mode-hook (lambda () (flymake-mode -1)))
(provide 'eglot-settings)

Python

(require 'python)

(setq
 python-shell-interpreter "python3"
 python-shell-interpreter-args "-i")

(define-key python-mode-map (kbd "C-c C-c") nil)

(use-package
 highlight-indent-guides
 :hook (python-mode . highlight-indent-guides-mode))

(add-hook
 'python-mode-hook
 (lambda ()
   (mapc
    (lambda (pair) (push pair prettify-symbols-alist))
    '( ;; Syntax
      ;; ("def" .      #x2131)
      ;; ("not" .      #x2757)
      ("in" . #x2208)
      ;; ("not in" .   #x2209)
      ("return" . #x27fc) ("yield" . #x27fb)
      ;; ("for" .      #x2200)
      ;; Base Types
      ;; ("int" .      #x2124)
      ;; ("float" .    #x211d)
      ;; ("str" .      #x1d54a)
      ;; ("True" .     #x1d54b)
      ;; ("False" .    #x1d53d)
      ;; Mypy
      ;; ("Dict" .     #x1d507)
      ;; ("List" .     #x2112)
      ;; ("Tuple" .    #x2a02)
      ;; ("Set" .      #x2126)
      ;; ("Iterable" . #x1d50a)
      ;; ("Any" .      #x2754)
      ;; ("Union" .    #x22c3)
      ))))

;; python mode keymap
(define-key python-mode-map (kbd "C-c C-p") nil)

(use-package
 py-isort
 :config
 (define-key python-mode-map (kbd "M-I") 'py-isort-buffer)
 (define-key python-ts-mode-map (kbd "M-I") 'py-isort-buffer))

(use-package
 pip-requirements
 :init
 (add-to-list
  'auto-mode-alist
  `(,(rx "requirements" (zero-or-more anything) ".in" string-end)
    . pip-requirements-mode)))

Python Docstrings

(use-package
 buftra
 :straight
 '(buftra.el :type git :host github :repo "humitos/buftra.el"))

(use-package
 py-pyment
 :straight
 '(py-cmd-buffer.el
   :type git
   :host github
   :repo "humitos/py-cmd-buffer.el")
 :after python
 :config (setq py-pyment-options '("--output=google")))

MsPyls LSP

Not using this in favor of Pyright

(use-package
 lsp-python-ms
 :config
 (setq lsp-python-ms-cache "System")
 (setq lsp-python-ms-completion-add-brackets t)
 (setq lsp-python-ms-nupkg-channel "beta")
 (setq lsp-python-ms-log-level "Warning")
 (setq lsp-document-sync-method nil)
 (setq lsp-auto-execute-action nil)

 (setq lsp-python-ms-auto-install-server t)

 (add-to-list 'company-lsp-filter-candidates '(mspyls . t)))

Pyright LSP

(add-hook 'python-mode-hook #'lsp-deferred)
(setq lsp-diagnostics-disabled-modes
      '(python-mode python-mode python-ts-mode))
(use-package
  lsp-pyright
  :straight
  '(lsp-pyright :type git :host github :repo "emacs-lsp/lsp-pyright")
  :init (setq lsp-pyright-multi-root nil)
  (setq lsp-pyright-auto-import-completions nil)
  (setq lsp-pyright-diagnostic-mode "workspace")
  (setq lsp-pyright-typechecking-mode "basic")
  (setq lsp-pyright-disable-organize-imports t)
  :config (setq lsp-pyright-log-level "trace"))

Copying lines as a single line for pasting into the pdbpp debugger

(defun python-multiline-to-singleline ()
  "Convert multi-line Python code in the current region to a single line with single spaces."
  (interactive)
  (when (use-region-p)
    (let* ((start (region-beginning))
           (end (region-end))
           (multi-line-code (buffer-substring start end))
           (single-line-code (replace-regexp-in-string "[ \t\n]+" " " multi-line-code)))
      (kill-new single-line-code)
      (message "Single-line code copied to kill ring."))))

(with-eval-after-load 'python
  (define-key python-mode-map (kbd "C-c C-l") 'python-multiline-to-singleline))

Large Language Models in Emacs

Github Copilot

(use-package
 copilot
 :straight
 (:host github :repo "zerolfx/copilot.el" :files ("dist" "*.el"))
 :bind
 (:map
  copilot-completion-map (("M-<return>" . copilot-accept-completion)))
 :hook (prog-mode . copilot-mode)
 :hook (yaml-mode . copilot-mode)
 :hook (org-mode . copilot-mode)
 :config
 (setq copilot-max-char -1)
 (setq copilot-indent-offset-warning-disable t)
 :ensure t)

C3PO

This one didn’t work very well

(use-package
 c3po
 :straight (:host github :repo "d1egoaz/c3po.el")
 :config
 (setq chat-api-key
       (f-read-text (expand-file-name "~/.openai/emacs-key.txt"))))

OpenAI

(use-package
 openai
 :straight
 (openai :type git :host github :repo "emacs-openai/openai")
 :init (setq openai-key (getenv "OPENAI_API_KEY")))
(use-package
 chatgpt
 :straight (chatgpt :type git :host github :repo "emacs-openai/chatgpt")
 ;; :config (setq chatgpt-model "gpt-4-0613")
 )
(use-package
 codegpt
 :straight
 (codegpt :type git :host github :repo "emacs-openai/codegpt"))
(use-package
 dall-e
 :straight
 (dall-e :type git :host github :repo "emacs-openai/dall-e"))

Ellama

(use-package llm)
(use-package
 ellama
 :init
 (setopt
  ellama-provider (make-llm-ollama :chat-model "codellama:34b")))

Flycheck

(use-package
 flycheck
 :init
 (define-fringe-bitmap 'my-flycheck-fringe-indicator
   (vector
    #b00000000
    #b00000000
    #b00000000
    #b00000000
    #b00000000
    #b00000000
    #b00000000
    #b00011100
    #b00111110
    #b00111110
    #b00111110
    #b00011100
    #b00000000
    #b00000000
    #b00000000
    #b00000000
    #b00000000))
 (flycheck-define-error-level
  'error
  :severity 2
  :overlay-category 'flycheck-error-overlay
  :fringe-bitmap 'my-flycheck-fringe-indicator
  :fringe-face 'flycheck-fringe-error)
 (flycheck-define-error-level
  'warning
  :severity 1
  :overlay-category 'flycheck-warning-overlay
  :fringe-bitmap 'my-flycheck-fringe-indicator
  :fringe-face 'flycheck-fringe-warning)
 (flycheck-define-error-level
  'info
  :severity 0
  :overlay-category 'flycheck-info-overlay
  :fringe-bitmap 'my-flycheck-fringe-indicator
  :fringe-face 'flycheck-fringe-info)
 ;; :config
 ;; (use-package flycheck-pos-tip )
 ;; (flycheck-pos-tip-mode)
 )

; Flycheck
(setq flycheck-idle-change-delay 0.1)
(setq flycheck-display-errors-delay 0.1)
(setq flycheck-idle-buffer-switch-delay 0.1)

(setq flycheck-checkers (remove 'python-pylint flycheck-checkers))
(setq flycheck-checkers (remove 'python-pycompile flycheck-checkers))
(setq flycheck-checkers (remove 'python-pyright flycheck-checkers))

(add-hook 'after-init-hook #'global-flycheck-mode)
(setq flycheck-global-modes
      '(python-base-mode js-mode python-mode python-ts-mode))

Toggling flycheck buffer with “M-C”

I have my own little hook to open the flycheck buffer with M-C, and close it again with another M-C keystroke.

(defvar should-delete-flycheck-list-buffer nil)
(defun my/flycheck-list-errors ()
  "Open flycheck list if it doesn't exist.  If it does, close it."
  (interactive)
  (let* ((target-buffer-name "*Flycheck errors*")
         (target-buffer (get-buffer target-buffer-name))
         (target-window (get-buffer-window target-buffer)))
    (if (and target-buffer target-window)
        ;; the target buffer exists and window is visible
        (progn
          (when should-delete-flycheck-list-buffer
            (delete-window target-window))
          (kill-buffer target-buffer))
      ;; the target buffer doesn't exist or the window isn't visible
      (let* ((starting-window-count (count-windows)))
        (flycheck-list-errors)
        (setq should-delete-flycheck-list-buffer
              (> (count-windows) starting-window-count))))))

(define-key flycheck-mode-map (kbd "M-C") 'my/flycheck-list-errors)

Ruff in flycheck

Credit to abo-abo Update: Now supported as part of the official flycheck package.

;; From https://github.com/flycheck/flycheck/issues/1974#issuecomment-1343495202
(flycheck-define-checker
 python-ruff
 "A Python syntax and style checker using the ruff utility.
  To override the path to the ruff executable, set
  `flycheck-python-ruff-executable'.
  See URL `http://pypi.python.org/pypi/ruff'."
 :command
 ("ruff" "check"
  (eval
   (when buffer-file-name
     (concat "--stdin-filename=" buffer-file-name)))
  "-")
 :standard-input t
 :error-filter
 (lambda (errors)
   (let ((errors (flycheck-sanitize-errors errors)))
     (seq-map #'flycheck-flake8-fix-error-level errors)))
 :error-patterns
 ((warning
   line-start
   (file-name)
   ":"
   line
   ":"
   (optional column ":")
   " "
   (id (one-or-more (any alpha)) (one-or-more digit))
   " "
   (message (one-or-more not-newline))
   line-end))
 :modes (python-mode python-ts-mode))

(eval-after-load 'flycheck
  '(add-to-list 'flycheck-checkers 'python-ruff))

TRAMP

General Settings

(setq enable-remote-dir-locals 't)
(setq tramp-chunksize 4050)
(setq tramp-verbose 10)
(setq vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)"
                                   vc-ignore-dir-regexp
                                   tramp-file-name-regexp))

Magit in Tramp

Local PATH

(defun my-setup-tramp-path ()
  (let ((local-path (cdr (assoc 'my-project-specific-path dir-local-variables-alist))))
    (when local-path
      (add-to-list 'tramp-remote-path
                   (concat "/sshx:your_username@remote_host:" local-path)))))
(add-hook 'hack-dir-local-variables-hook #'my-setup-tramp-path)

Lock files

Disable lock files in TRAMP

(defun my-tramp-file-name-handler (operation &rest args)
  "Disable file locks for TRAMP files."
  (if (eq operation 'vc-registered)
      nil
    (let ((file-name-handler-alist
           (remove (cons "\\`/\\(ssh\\|scp\\|ftp\\):" 'my-tramp-file-name-handler)
                   file-name-handler-alist)))
      (apply operation args))))

(add-to-list 'file-name-handler-alist
             '("\\`/\\(ssh\\|scp\\|ftp\\):" . my-tramp-file-name-handler))

Dir-locals helper function

I’m still trying to make all my editing features work inside TRAMP with Emacs.

(defun set-remote-venv-path ()
  "Set PATH for TRAMP to include the venv/bin directory."
  (with-parsed-tramp-file-name default-directory tramp-info
    (let* ((absolute-local-directory (if (string= tramp-info-localname "~/")
                                         (tramp-call-process tramp-info-method tramp-info-user tramp-info-host "pwd" nil t nil)
                                       tramp-info-localname))
           (remote-path (concat "/" tramp-info-method ":" tramp-info-user "@" tramp-info-host ":" absolute-local-directory)))
      (set (make-local-variable 'process-environment)
           (append
            (list (format "PATH=%svenv/bin:%s"
                          remote-path
                          (getenv "PATH")))
            process-environment)))))
(defun set-remote-venv-path ()
  "Set PATH for TRAMP to include the venv/bin directory."
  (with-parsed-tramp-file-name default-directory tramp-info
    (let ((remote-path (if (string= tramp-info-localname "~/")
                           (concat "/" tramp-info-method ":" tramp-info-user "@" tramp-info-host ":/")
                         (concat "/" tramp-info-method ":" tramp-info-user "@" tramp-info-host ":" tramp-info-localname))))
      (set (make-local-variable 'process-environment)
           (append
            (list (format "PATH=%svenv/bin:%s"
                          remote-path
                          (getenv "PATH")))
            process-environment)))))

Direnv in Tramp

Code exists in an unmerged branch.

(defcustom my-direnv-enabled-hosts nil
  "List of remote hosts to use Direnv on.

Each host must have `direnv' executable accessible in the default
environment."
  :type '(repeat string)
  :group 'my)

(defun tramp-sh-handle-start-file-process@my-direnv (args)
  "Enable Direnv for hosts in `my-direnv-enabled-hosts'."
  (with-parsed-tramp-file-name
   (expand-file-name default-directory) nil
   (if (member host my-direnv-enabled-hosts)
       (progn (pcase-let ((`(,name ,buffer ,program . ,args) args))
         `(,name ,buffer "direnv" "exec" ,localname ,program ,@args)) (debug))
     args)))

(with-eval-after-load "tramp-sh"
  (advice-add
   'tramp-sh-handle-start-file-process
   :filter-args #'tramp-sh-handle-start-file-process@my-direnv))

Java/Javascript/JSON

(use-package jq-mode)
(use-package
 jsonian
 :straight '(jsonian :type git :host github :repo "iwahbe/jsonian" :build (:not autoloads)))

Org Mode

(defun org-src-format-and-save ()
  (interactive)
  (when (provided-mode-derived-p major-mode 'python-base-mode)
    (if (and (boundp 'apheleia-formatter)
             (seq-contains-p apheleia-formatter 'ruff))
        (apheleia-format-buffer 'ruff)
      (apheleia-format-buffer 'black)))
  (when (eq major-mode 'emacs-lisp-mode)
    (elisp-autofmt-buffer))
  (sit-for 0.100)
  (org-edit-src-save))
(use-package
 org
 :defer t
 :hook (org-mode-hook . visual-line-mode)
 :hook (org-babel-after-execute-hook . org-display-inline-images)
 :hook (org-babel-after-execute-hook . append)
 :bind
 (:map
  org-mode-map
  ;; ("\C-cl" . org-store-link)
  ;; ("\C-ca" . org-agenda)
  ("C-'" . nil)
  ("C-c C-c" . org-ctrl-c-ctrl-c)
  ("C-M-<return>" . my/org-babel-execute-and-next)
  ("C-c <" . nil)
  :map
  org-src-mode-map
  ("C-x C-s" . org-src-format-and-save))
 :config
 (setq org-format-latex-options
       (plist-put org-format-latex-options :scale 2.0))
 (setq org-latex-create-formula-image-program 'dvisvgm)


 (org-babel-do-load-languages
  'org-babel-load-languages
  '((emacs-lisp . t)
    (sqlite . t)
    (shell . t)
    (jq . t)
    (jupyter . nil)
    (python . t)))

 (defun my/load-org-jupyter ()
   (org-babel-do-load-languages
    'org-babel-load-languages '((jupyter . t)))
   (org-babel-jupyter-aliases-from-kernelspecs 'refresh))
 (visual-line-mode t)

 (setq org-log-done t)

 ;; adding this does some weird stuff in colors
 (setq org-startup-indented nil)

 ;; fontify code in code blocks
 (setq org-src-fontify-natively t)
 (setq org-confirm-babel-evaluate nil) ;don't prompt me to confirm everytime I want to evaluate a block
 (setq org-src-preserve-indentation t)

 (defun my-update-direnv-in-org-src ()
   "Update direnv environment variables for org-src buffers."
   (when (and (bound-and-true-p direnv-mode)
              (eq major-mode 'org-mode))
     (let ((org-file-path (buffer-file-name (buffer-base-buffer))))
       (when org-file-path
         (direnv-update-environment org-file-path)))))

 (add-hook 'org-src-mode-hook 'my-update-direnv-in-org-src))

Org Babel

(setq ob-ipython-command "jupyter")
(use-package
 plantuml-mode
 :after org
 :config
 (setq plantuml-default-exec-mode 'executable)
 (setq org-plantuml-exec-mode 'plantuml)
 (add-to-list 'auto-mode-alist '("\\.plantuml\\'" . plantuml-mode)))
(use-package ob-napkin)
(use-package mermaid-mode)
(use-package ob-mermaid)
(use-package htmlize :after org)

(defun my/org-babel-execute-and-next ()
  (interactive)
  (org-babel-execute-src-block)
  (org-babel-next-src-block))

Colors in Org

(use-package
 org-bullets
 :after org
 :hook (org-mode-hook . org-bullets-mode))

;;nil means to wrap lines in org mode
(setq org-startup-truncated nil)

Org Modern

I thought I liked this at first, but now I’m going to disable it because of some annoying text interactions.

(use-package org-modern :after org :config (global-org-modern-mode))
(use-package
 org-modern-indent
 :after org
 :straight
 '(org-modern-indent
   :type git
   :host github
   :repo "jdtsmith/org-modern-indent")
 :config
 (setq org-startup-indented t)
 (add-hook 'org-mode-hook #'org-modern-indent-mode 90))

Org Roam

I’m not currently using Org Roam, but maybe some day I’d like to.

(straight-use-package 'org-roam)
(use-package
 org-roam
 :ensure t
 :custom (org-roam-directory (file-truename "~/Documents/PERSONAL/ORG_ROAM/"))
 :bind
 (("C-c n l" . org-roam-buffer-toggle)
  ("C-c n f" . org-roam-node-find)
  ("C-c n g" . org-roam-graph)
  ("C-c n i" . org-roam-node-insert)
  ("C-c n c" . org-roam-capture)
  ;; Dailies
  ("C-c n j" . org-roam-dailies-capture-today))
 :config (org-roam-setup) (require 'org-roam-protocol))

Jupyter

(use-package
 zmq
 :straight '(zmq :host github :repo "nnicandro/emacs-zmq")
 :init
 ; macro to wrap loading
 (defmacro safe-wrap (fn &rest clean-up)
   `(unwind-protect
        (let (retval)
          (condition-case ex
              (setq retval
                    (progn
                      ,fn))
            ('error
             (message (format "Caught exception: [%s]" ex))
             (setq retval (cons 'exception (list ex)))))
          retval)
      ,@clean-up))

 (defun fix-zmq-file-naming ()
   "copy .so to .dylib so that we can proceed with installing zmq"
   (let* ((tag (concat "tags/" zmq-emacs-version))
          (api-url
           "https://api.github.com/repos/nnicandro/emacs-zmq/")
          (repo-url "https://github.com/nnicandro/emacs-zmq/")
          (release-url (concat api-url "releases/"))
          (info
           (zmq--download-url
            (concat release-url tag) (require 'json)
            (let ((json-object-type 'plist))
              (ignore-errors
                (json-read)))))
          (tag-name
           (or (plist-get info :tag_name) (throw 'failure nil)))
          (ezmq-sys (concat "emacs-zmq-" (zmq--system-configuration)))
          (assets
           (cl-remove-if-not
            (lambda (x) (string-prefix-p ezmq-sys x))
            (mapcar
             (lambda (x) (plist-get x :name))
             (append (plist-get info :assets) nil)))))
     (when assets
       (let ((default-directory
              (file-name-directory (locate-library "zmq"))))
         ;; We have a signature file and a tar.gz file for each binary so the
         ;; minimum number of files is two.
         (if (> (length assets) 2)
             (error "TODO More than one file found")
           (let* ((tgz-file
                   (cl-find-if
                    (lambda (x) (string-suffix-p "tar.gz" x)) assets))
                  (lib
                   (expand-file-name (concat
                                      "emacs-zmq" module-file-suffix)
                                     (expand-file-name
                                      (file-name-sans-extension
                                       (file-name-sans-extension
                                        tgz-file))))))
             (let* ((source-file
                     (concat (file-name-sans-extension lib) ".so")))
               (when (not (f-exists? lib))
                 (print (format "Copy from %s to %s" source-file lib))
                 (copy-file source-file lib)))
             t))))))

 (let (original-noninteractive-value
       noninteractive)
   ;; this is a hack so i don't have to ask about downloading the compatible binary
   (setq noninteractive t)
   (safe-wrap
    (condition-case nil
        (require 'zmq)
      (error
       (fix-zmq-file-naming)
       (require 'zmq)))) ;; set the variable back to its original value
   (setq noninteractive original-noninteractive-value)))

(use-package
 jupyter
 :defer t
 :custom (jupyter-repl-echo-eval-p t)
 :config
 (defun my-switch-to-python-ts-mode-if-jupyter ()
   "Switches to python-ts-mode if the org source block is of type jupyter-python."
   (when (and (eq major-mode 'python-mode)
              (string=
               (org-element-property
                :language (org-element-at-point))
               "jupyter-python"))
     (python-ts-mode)))

 (add-hook
  'org-src-mode-hook 'my-switch-to-python-ts-mode-if-jupyter)

 ;; (add-to-list 'org-src-lang-modes '("python" . python-ts))
)

Org Markdown

This is really only used in the publishing script of my emacs config, I don’t need to load this on every startup.

ox-md

(use-package ox-jekyll-md :init (setq org-jekyll-md-include-yaml-front-matter nil))

org-jekyll-lite

This is really only used in the publishing script of my emacs config, I don’t need to load this on every startup.

(use-package
 ox-jekyll-lite
 :after org
 :straight
 '(ox-jekyll-lite
   :type git
   :host github
   :repo "jsigman/ox-jekyll-lite"))

Org Appear

(use-package
 org-appear
 :straight
 '(org-appear :type git :host github :repo "awth13/org-appear")
 :after org
 :hook (org-mode-hook . org-appear-mode))

Org Ref

(use-package request)
(use-package
 org-ref
 :after org
 :config
 (setq org-latex-pdf-process
       (list "latexmk -shell-escape -bibtex -f -pdf %f")))

Magit and Version Control

Magit

(use-package
 magit
 :after (projectile)
 :demand t
 :init
 (setq magit-delete-by-moving-to-trash nil)
 (setq vc-handled-backends nil)

 (eval-after-load 'magit-branch
   '(progn
      (advice-add
       'magit-checkout
       :after (lambda (&rest args) (projectile-invalidate-cache nil)))
      (advice-add
       'magit-branch-and-checkout
       :after
       (lambda (&rest args) (projectile-invalidate-cache nil)))))
 :bind
 (:map
  magit-mode-map
  ("C-x g" . nil)
  ("M-G" . nil)
  ("M-0" . nil)
  ("M-1" . nil)
  ("M-2" . nil)
  ("M-3" . nil))
 :bind (:map global-map ("M-G" . magit-status))
 :config (use-package transient))

Forge

Haven’t yet found a great use for this.

(use-package
 forge
 :after magit
 :config
 ; This is so we can access forge information
 (setq auth-sources '("~/.authinfo")))

Magit Todos mode

This is currently broken for me.

(use-package magit-todos :config (magit-todos-mode t))

Magit coloring

git-gutter

(use-package
 git-gutter+
 :config (global-git-gutter+-mode t)

 (setq git-gutter+-modified-sign " ") ;; two space
 (setq git-gutter+-added-sign "+") ;; multiple character is OK
 (setq git-gutter+-deleted-sign "-")

 (set-face-foreground 'git-gutter+-modified "magenta")
 (set-face-foreground 'git-gutter+-added "dark green")
 (set-face-foreground 'git-gutter+-deleted "red"))

(use-package pcre2el)
(use-package async)
(use-package hl-todo)
(use-package rg)
(use-package ag)

(use-package ghub :defer t)

; add this so that git gutter plus does not screw up tramp sessions
; it disables GGP during tramp
(defun git-gutter+-refresh ()
  (git-gutter+-clear)
  (when (not (file-remote-p (buffer-file-name)))
    (let ((file (buffer-file-name)))
      (when (and file (file-exists-p file))
        (if (file-remote-p file)
            (let* ((repo-root (git-gutter+-root-directory file))
                   (default-directory
                    (git-gutter+-remote-default-directory
                     repo-root file)))
              (git-gutter+-process-diff
               (git-gutter+-remote-file-path repo-root file)))
          (git-gutter+-process-diff
           (git-gutter+-local-file-path file)))))))

diff-hl

(use-package
 diff-hl
 :config (global-diff-hl-mode)
 (add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh)
 (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh))

Ediff

(use-package
 ediff
 :defer t
 :init (setq ediff-split-window-function 'split-window-horizontally)
 (setq ediff-window-setup-function
       'ediff-setup-windows-plain)
 (setq ediff-diff-options "-w"))

Multiple Cursors (MC)

(use-package
 multiple-cursors
 :config
 (global-set-key (kbd "C-M-j") 'mc/edit-lines)
 (global-set-key (kbd "C->") 'mc/mark-next-like-this)
 (global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
 (global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
 (global-set-key (kbd "C-M-=") 'mc/mark-all-symbols-like-this))

Docker

(use-package
 dockerfile-mode
 :config (setq dockerfile-mode-command "docker"))
(use-package docker
  :ensure t
  :bind ("C-c d" . docker))

Debug Adapter Protocal (DAP)

I’m currently not using DAP mode, and prefer to use python from the command line (vterm) with pdb. Some day I’d like to learn this.

(use-package
 dap-mode
 :init
 (if (eq system-type 'gnu/linux)
     (add-to-list 'image-types 'svg))
 :config
 (add-hook
  'dap-stopped-hook (lambda (arg) (call-interactively #'dap-hydra)))
 ;; Enabling only some features
 (setq dap-auto-configure-features
       '(sessions locals controls tooltip))
 (setq dap-python-debugger 'debugpy)
 (require 'dap-mode)
 (require 'dap-python)
 (require 'dap-ui)

 (add-hook 'python-mode-hook 'dap-mode)
 (add-hook 'python-mode-hook 'dap-ui-mode)
 (add-hook 'python-mode-hook 'dap-tooltip-mode)

 (define-key python-mode-map (kbd "M-D") #'dap-hydra))

Treemacs

(use-package
 treemacs
 :bind (("M-P" . treemacs))
 :config (setq treemacs-persist-file "treemacs-persist"))
(use-package treemacs-projectile :after treemacs)
(use-package
 treemacs-icons-dired
 :after
 treemacs
 dired
 :config (treemacs-icons-dired-mode))

Markdown

(use-package
 markdown-mode
 :mode ("README\\.md\\'" . gfm-mode)
 :init (setq markdown-command "multimarkdown")
 :hook (markdown-mode . lsp-deferred)
 :config)

HTML

(when (not (eq system-type 'gnu/linux))
  (require 'html-ts-mode)
  (define-key html-ts-mode-map (kbd "M-o M-o") nil)
  (define-key html-mode-map (kbd "M-o M-o") nil)
  (define-key sgml-mode-map (kbd "M-o M-o") nil)
  (define-key html-mode-map (kbd "M-o") nil)
  (define-key sgml-mode-map (kbd "M-o") nil)
  (define-key html-mode-map (kbd "M-o") 'next-multiframe-window)
  (define-key html-mode-map (kbd "M-i") 'previous-multiframe-window)

  (define-key html-ts-mode-map (kbd "M-o M-o") nil)
  (define-key html-ts-mode-map (kbd "M-o") nil)
  (define-key html-ts-mode-map (kbd "M-o") 'next-multiframe-window)
  (define-key
   html-ts-mode-map (kbd "M-i") 'previous-multiframe-window))

Web mode

(use-package
 web-mode
 :config
 (add-to-list 'auto-mode-alist '("\\.liquid\\'" . web-mode)))

Lua

Non-gnu ELPA version

(use-package
 lua-mode
 :ensure t
 :mode ("\\.lsyncd\\.conf\\'" . lua-mode))

lua-ts-mode

(use-package
 lua-ts-mode
 :straight
 '(lua-ts-mode
   :type git
   :host nil
   :repo "https://git.sr.ht/~johnmuhl/lua-ts-mode"
   :files ("*.el"))
 :mode (("\\.lua\\'" . lua-ts-mode) ("lsyncd\\.conf\\'" . lua-ts-mode))
 :config
 ;; Any additional configuration you might want goes here
 )

Tree-Sitter

Tree-Sitter mode

This is the old way of using tree-sitter in emacs. In emacs 29, this moved to treesit.

(use-package
 tree-sitter
 :config (global-tree-sitter-mode t)
 :hook ((tree-sitter-after-on-hook . tree-sitter-hl-mode) (python-mode . tree-sitter-hl-mode)))
(use-package tree-sitter-langs)

Combobulate

Combobulate seems like a cool idea, but I’ve found it to be more frustrating than helpful in python.

(use-package
 combobulate
 :straight '(combobulate :type git :host github :repo "mickeynp/combobulate")
 ;; You can manually enable Combobulate with `M-x
 ;; combobulate-mode'.
 :hook
 ((python-ts-mode . combobulate-mode)
  (js-ts-mode . combobulate-mode)
  (css-ts-mode . combobulate-mode)
  (yaml-ts-mode . combobulate-mode)
  (typescript-ts-mode . combobulate-mode)
  (tsx-ts-mode . combobulate-mode))
 ;; Amend this to the directory where you keep Combobulate's source
 ;; code.
 :bind
 (:map
  combobulate-key-map (("C-;" . combobulate-avy-jump) ("M-k" . nil))))

Treesit

(defun copy-different-key-bindings (source-map dest-map)
  "Copy key bindings from SOURCE-MAP to DEST-MAP, but only where they differ."
  (map-keymap
   (lambda (binding value)
     (unless (eq value (lookup-key dest-map (vector binding)))
       (define-key dest-map (vector binding) value)))
   source-map))


(use-package
 treesit-auto
 :straight '(treesit-auto :type git :host github :repo "renzmann/treesit-auto")
 :demand t
 :init
 (setq treesit-auto-install t)
 (setq treesit-auto-langs
       '(awk
         bash
         bibtex
         c
         cmake
         commonlisp
         cpp
         css
         dockerfile
         html
         java
         javascript
         json
         latex
         make
         markdown
         proto
         python
         ruby
         toml
         typescript
         yaml))
 :config (global-treesit-auto-mode)
 ;; set up treesit mode hooks for relevant modes
 (setq python-ts-mode-hook python-mode-hook)
 (setq yaml-ts-mode-hook yaml-mode-hook)
 (setq json-ts-mode-hook json-mode-hook)
 ;; (setq js-ts-mode-hook js-mode-hook)
 (setq markdown-ts-mode-hook markdown-mode-hook)
 (setq bash-ts-mode-hook sh-mode-hook)
 ;; Then call the function like this:
 (copy-different-key-bindings python-mode-map python-ts-mode-map)
 ;; (copy-different-key-bindings yaml-mode-map yaml-ts-mode-map)
 ;; (copy-different-key-bindings json-mode-map json-ts-mode-map)
 ;; (copy-different-key-bindings markdown-mode-map markdown-ts-mode-map)
 (copy-different-key-bindings sh-mode-map bash-ts-mode-map))

Modeline

Telephone Line

(use-package
 telephone-line
 :config
 (setq telephone-line-lhs
       '((evil . (telephone-line-evil-tag-segment))
         (accent
          .
          (telephone-line-vc-segment
           telephone-line-erc-modified-channels-segment
           telephone-line-process-segment))
         (nil
          .
          (telephone-line-minor-mode-segment
           telephone-line-buffer-segment))))
 (setq telephone-line-rhs
       '((nil . (telephone-line-misc-info-segment))
         (accent . (telephone-line-major-mode-segment))
         (evil . (telephone-line-airline-position-segment))))
 (telephone-line-mode t))

Doom Modeline

Bugfix: Requires installing shrink path manually on linux. Not sure why.

(use-package
 shrink-path
 :straight
 '(shrink-path :type git :host github :repo "zbelial/shrink-path.el"))
(use-package doom-modeline :config (doom-modeline-mode 1) (setq doom-modeline-unicode-fallback t))

Awesome Tray

(use-package
 awesome-tray
 :straight
 '(awesome-tray
   :type git
   :host github
   :repo "manateelazycat/awesome-tray")
 :init
 (setq awesome-tray-active-modules
       '("buffer-name" "mode-name" "belong")) ;TODO: add "belong" back in after treesit
 :config (awesome-tray-mode 1) awesome-tray-active-modules)

Diminish/Blackout

(use-package diminish)
(diminish 'anzu-mode)
(diminish 'ivy-mode)
(diminish 'rainbow-mode)
(diminish 'eldoc-mode)
(diminish 'projectile-mode)
(diminish 'blacken-mode)
(diminish 'python-black-on-save-mode)
(diminish 'which-key-mode)
(diminish 'flycheck-mode)
(add-hook 'undo-tree-mode-hook (lambda () (diminish 'undo-tree-mode)))
(diminish 'auto-revert-mode)
(diminish 'beacon-mode)
(diminish 'all-the-icons-dired-mode)
(diminish 'tree-sitter-hl-mode)

(use-package blackout)
(blackout 'ivy-mode)
(blackout 'company-posframe-mode)
(blackout 'company-mode)
(diminish 'yas-mode)
(blackout 'yas-minor-mode)
(blackout 'rainbow-mode)
(blackout 'which-key-mode)
(blackout 'projectile-mode)
(blackout 'git-gutter-mode)
(blackout 'git-gutter+-mode)
(blackout 'goggles-mode)
(blackout 'org-roam-mode)
(blackout 'polymode)
(blackout 'poly-org-mode)
(blackout 'smartparens-mode)
(blackout 'visual-line-mode)
(blackout 'anzu-mode)
(blackout 'apheleia-mode)
(blackout 'explain-pause-mode)
(blackout 'volatile-highlights-mode)
(blackout 'all-the-icons-dired-mode)
(blackout 'jupyter-repl-interaction-mode)
(blackout 'page-break-lines-mode)
(blackout 'org-src-mode)
(blackout 'whole-line-or-region-local-mode)
(blackout 'smartparens-mode)
(blackout 'tree-sitter-hl-mode)
(blackout 'tree-sitter-mode)
(blackout 'highlight-indent-guides-mode)
(blackout 'copilot-mode)

(diminish 'jupyter-repl-interaction-mode)
(diminish 'page-break-lines-mode)
(diminish 'org-src-mode)
(diminish 'highlight-indent-guides)

Colors and Theme

;; Treat all themes as safe; no query before use.
(setf custom-safe-themes 't)
(setq frame-title-format nil)

Emacs Theme

Leuven

Leuven is always a nice option, beautiful with Org Mode.

(use-package leuven-theme
  :config
  (load-theme 'leuven-dark))

Catpuccin Theme

Trying this theme out for a little bit.

(use-package
 catppuccin-theme
 :config
 (load-theme 'catppuccin :no-confirm)
 (setq catppuccin-flavor 'frappe)
 ;; (setq catppuccin-flavor 'latte)
 ;; (setq catppuccin-flavor 'macchiato)
 ;; (setq catppuccin-flavor 'mocha)
 (catppuccin-reload))

ef-day

I’m always changing my theme, but I tend to like light themes with yellow-ish backgrounds. Almost like handwriting with a yellow memo pad.

(use-package ef-themes :config (load-theme 'ef-day t))

Gruvbox-dark

(use-package
 gruvbox-theme
 :ensure t
 :config (load-theme 'gruvbox-dark-hard :no-confirm)
 ;; :after (centered-window-mode) :hook (centered-window-mode . cwm-update-fringe-background)
)

Nano

(use-package
 nano
 :straight
 (nano-emacs :type git :host github :repo "rougier/nano-emacs")
 :config (require 'nano))

Colors in Dired

(use-package rainbow-mode :hook (LaTeX-mode . rainbow-mode))
(use-package dired-hacks :hook (emacs-lisp-mode . rainbow-mode))

Catch if running centered-window-mode

This may be needed to set the fringe to the new background color. Run it when startup is finished.

(add-hook 'emacs-startup-hook
          (lambda ()
            (when (fboundp 'cwm-update-fringe-background)
              (cwm-update-fringe-background))))



Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Indicating which blocks are loaded in webpage literate elisp
  • Why we can interpret softmax scores as probabilities
  • Hosting my CV with github actions
  • Literate emacs config as a webpage
  • Studying Eigenvalues of Rotation Group Matrices