All Projects → terlar → emacs-config

terlar / emacs-config

Licence: MIT license
Emacs configuration

Programming Languages

emacs lisp
2029 projects
Nix
1067 projects
YASnippet
69 projects
shell
77523 projects
HTML
75241 projects
go
31211 projects - #10 most used programming language

GNU Emacs Configuration

Overview

Terje’s GNU Emacs configuration. It is not very well documented right now, but hopefully I will have time to write down some more details later.

This configuration is making a lot of assumptions as it is built as a package with Nix. This means that all packages are expected to be on the load-path already and compatibility is only guaranteed with the GNU Emacs version that is part of this package. Currently GNU Emacs 29.0.50.

The rough details:

themes
readable-typo-theme and readable-mono-theme
fonts
Iosevka Slab, Noto Sans, Noto Serif
buffer completion
corfu
minibuffer completion
vertico
package configuration
use-package
key definitions
general
code navigation
dumb-jump, source-peek, imenu-anywhere
syntax checker
flymake
persistent undo
via built-in desktop-save-mode
language server integration
eglot

Screenshots

org-mode

./media/org-mode.svg

markdown-mode

./media/markdown-mode.svg

emacs-lisp-mode

./media/emacs-lisp-mode.svg

Early initialization

;;; early-init.el --- Early Initialization -*- lexical-binding: t; -*-

Startup

Time the startup and display the startup time after completed.

(add-hook 'emacs-startup-hook
          (lambda ()
            (message "Loaded Emacs in %.03fs"
                     (float-time (time-subtract after-init-time before-init-time)))))

Temporarily reduce garbage collection to gain some performance boost during startup.

(let ((normal-gc-cons-threshold gc-cons-threshold)
      (normal-gc-cons-percentage gc-cons-percentage)
      (normal-file-name-handler-alist file-name-handler-alist)
      (init-gc-cons-threshold most-positive-fixnum)
      (init-gc-cons-percentage 0.6))
  (setq gc-cons-threshold init-gc-cons-threshold
        gc-cons-percentage init-gc-cons-percentage
        file-name-handler-alist nil)
  (add-hook 'after-init-hook
            `(lambda ()
               (setq gc-cons-threshold ,normal-gc-cons-threshold
                     gc-cons-percentage ,normal-gc-cons-percentage
                     file-name-handler-alist ',normal-file-name-handler-alist))))

Inhibit startup screen and messages. If you are new to Emacs it is recommended to not disable the startup screen as it has great content to get you going.

(setq inhibit-startup-echo-area-message t)
(setq inhibit-startup-screen t)
(setq initial-scratch-message nil)

Performance tweaks. Don’t load default library and use fundamental-mode to reduce amount of hooks.

(setq inhibit-default-init t)
(setq initial-major-mode 'fundamental-mode)

UI

Disable GUI components.

(setq use-dialog-box nil)
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)

Don’t implicitly resize frames when changing various settings.

(setq frame-inhibit-implied-resize t)

Ignore X resources.

(advice-add #'x-apply-session-resources :override #'ignore)

Base settings

;;; init.el --- Initialization -*- lexical-binding: t; -*-

Variables

Provide an easy way to toggle debug mode which will set certain variables to produce more informative output. It can be set either by providing the environment variable DEBUG or start Emacs with --debug-init.

(eval-and-compile
  (when (getenv "DEBUG") (setq init-file-debug t))
  (setq debug-on-error (and (not noninteractive) init-file-debug)))

Provide a location where Emacs can store data and cache.

(eval-and-compile
  (defvar data-dir
    (if (getenv "XDG_DATA_HOME")
        (concat (getenv "XDG_DATA_HOME") "/emacs/")
      (expand-file-name "~/.local/share/emacs/"))
    "Directory for data.")

  (defvar cache-dir
    (if (getenv "XDG_CACHE_HOME")
        (concat (getenv "XDG_CACHE_HOME") "/emacs/")
      (expand-file-name "~/.cache/emacs/"))
    "Directory for cache.")

  (defvar pictures-dir
    (or (getenv "XDG_PICTURES_DIR")
        (expand-file-name "~/Pictures/"))
    "Directory for pictures."))

Package management

Use generated package autoloads. These will be provided by the Nix package.

(defvar package-quickstart t)

Load path

Add local and private libraries to load-path.

(eval-and-compile
  (setq load-path
        (append (delete-dups load-path)
                (list (expand-file-name "lisp" user-emacs-directory)
                      (expand-file-name "private" user-emacs-directory)))))

Customization

Put custom definitions in a temporary file so it doesn’t grow over time. This means all customization will disappear upon reboot and instead needs to be managed via the initialization file.

(setq custom-file (expand-file-name "custom.el" temporary-file-directory))

use-package

(eval-when-compile
  (require 'use-package))

(eval-and-compile
  (defun use-package-ensure-ignore (&rest _args) t)
  (setq use-package-ensure-function #'use-package-ensure-ignore)

  (setq use-package-always-defer t)
  (setq use-package-hook-name-suffix nil))

(if init-file-debug
    (setq use-package-verbose t
          use-package-expand-minimally nil
          use-package-compute-statistics t)
  (setq use-package-verbose nil
        use-package-expand-minimally t))

auto-minor-mode

Enable minor modes by buffer name and contents. It provides the use-package keyword :minor and :magic-minor where you can specify these rules.

(use-package auto-minor-mode
  :ensure t
  :init
  (eval-when-compile
    (require 'auto-minor-mode)))

no-littering

Help keeping user-emacs-directory clean.

(use-package no-littering
  :ensure t
  :demand t
  :init
  (setq no-littering-etc-directory data-dir)
  (setq no-littering-var-directory cache-dir))

Performance

No second pass of case-insensitive search over auto-mode-alist.

(setq auto-mode-case-fold nil)

Give up some bidirectional functionality for slightly faster re-display.

(setq bidi-inhibit-bpa t)
(setq-default bidi-display-reordering 'left-to-right
              bidi-paragraph-direction 'left-to-right)

Update UI less frequently

(setq idle-update-delay 1.0
      jit-lock-defer-time 0)

Accelerate scrolling with the trade-off of sometimes delayed accurate fontification.

(setq fast-but-imprecise-scrolling t)

gcmh

Adjust Garbage Collector threshold depending on activity and try to run garbage collection during idle instead.

(use-package gcmh
  :ensure t
  :hook
  (after-init-hook . gcmh-mode)
  :init
  (setq gcmh-idle-delay 5)
  (setq gcmh-high-cons-threshold (* 16 1024 1024)) ; 16MB
  (setq gcmh-verbose init-file-debug))

explain-pause-mode

Track how much time is spent in each function and present a view to investigate this data.

(use-package explain-pause-mode
  :ensure t
  :commands
  (explain-pause-mode)
  :init
  (when init-file-debug
    (explain-pause-mode 1)))

so-long

Improve performance for files with long lines, for example minified code.

(use-package so-long
  :defer 2
  :config
  (global-so-long-mode 1))

vlf

Large file support. This can view/edit/search and compare large files.

(use-package vlf :ensure t)

Libraries

all-the-icons

Library for displaying icons. I am trying to use icons where possible.

(use-package all-the-icons
  :ensure t
  :commands
  (all-the-icons-faicon all-the-icons-octicon))

cl-lib

Library providing functions inspired by Common Lisp. In many cases these functions feel more natural to use.

(require 'cl-lib)
(require 'cl-extra)

flymake-quickdef

Library to quickly define backends for flymake.

(use-package flymake-quickdef :ensure t :commands (flymake-quickdef-backend))

hide-lines

Library to hide lines based on a regular expressions.

(use-package hide-lines
  :ensure t
  :commands (hide-lines-matching))

quick-peek

Library to display inline popups; used by source-peek.

(use-package quick-peek
  :ensure t
  :commands (quick-peek-show))

Functions

Prevent forms from producing output or other noise.

(defmacro quiet! (&rest forms)
  "Run FORMS without making any noise."
  `(if init-file-debug
       (progn ,@forms)
     (let ((message-log-max nil))
       (with-temp-message (or (current-message) "") ,@forms))))

(defun quiet-function-advice (orig-fn &rest args)
  "Advice used to make a function quiet.
Call ORIG-FN with ARGS and suppress the output.  Usage:

  (advice-add \\='orig-fn :around #\\='quiet-function-advice)"
  (quiet! (apply orig-fn args)))

Display

(defun display-ctrl-M-as-newline ()
  "Display `^M' as newline."
  (interactive)
  (setq buffer-display-table (make-display-table))
  (aset buffer-display-table ?\^M [?\n]))

Frame

Screenshot current frame in .svg or .png format.

(defun screenshot (type)
  "Save a screenshot of the current frame as an image in TYPE format.
Saves to a temp file and puts the filename in the kill ring."
  (let* ((ext (concat "." (symbol-name type)))
         (filename (make-temp-file "Emacs-" nil ext))
         (data (x-export-frames nil type)))
    (with-temp-file filename
      (insert data))
    (kill-new filename)
    (message filename)))

(defun screenshot-svg ()
  "Save a screenshot of the current frame as an SVG image.
Saves to a temp file and puts the filename in the kill ring."
  (interactive)
  (screenshot 'svg))

(defun screenshot-png ()
  "Save a screenshot of the current frame as an PNG image.
Saves to a temp file and puts the filename in the kill ring."
  (interactive)
  (screenshot 'png))

Process

(defun send-buffer-to-ssh ()
  "Send the whole buffer to the *ssh* process."
  (interactive)
  (process-send-region "*ssh*" (point-min) (point-max)))

(defun send-to-ssh ()
  "Send selected region or current line to the *ssh* process."
  (interactive)
  (let ((procbuf "*ssh*"))
    (if (use-region-p)
        (process-send-region procbuf (region-beginning) (region-end))
      (process-send-string procbuf (thing-at-point 'line t)))))

Window

Dedicated window:

(defun toggle-dedicated-window ()
  "Toggle selected window as dedicated window."
  (interactive)
  (set-window-dedicated-p (selected-window)
                          (not (window-dedicated-p (selected-window)))))

Fringe control:

(defun no-fringes ()
  "Remove all fringes."
  (interactive)
  (set-window-fringes (selected-window) 0 0 nil))

(defun restore-fringes ()
  "Restore fringes."
  (interactive)
  (set-window-fringes (selected-window) 8 8 t))

History

savehist

Persist history.

(use-package savehist
  :hook
  (after-init-hook . savehist-mode)
  :init
  (setq savehist-additional-variables
        '(kill-ring
          search-ring
          regexp-search-ring))
  (setq savehist-autosave-interval 60)
  (setq savehist-save-minibuffer-history t))

Localization

(setq-default calendar-week-start-day 1)

Shell

Set shell to bash as fish is not compatible with all packages using the shell.

(setq shell-file-name "bash")

UX

Disable bell (both visual and audible).

(setq ring-bell-function #'ignore)
(setq visible-bell nil)

Use y / n instead of yes / no.

(setq confirm-kill-emacs #'y-or-n-p)
(fset #'yes-or-no-p #'y-or-n-p)

Hide M-x commands which does not work in the current buffer.

(setq read-extended-command-predicate #'command-completion-default-include-p)

Appearance

Typography

Size

(defvar init-line-spacing 0.25
  "Spacing between lines.")

(defvar init-default-font-height 120
  "Default font height.")

Face

(defvar init-fixed-pitch-font "Iosevka Slab"
  "Font used for fixed-pitch faces.")

(defvar init-variable-pitch-font "Noto Sans"
  "Font used for variable-pitch faces.")

(defvar init-serif-font "Noto Serif"
  "Font used for serif faces.")

(defvar init-unicode-font "Noto Sans Mono"
  "Fallback font used for unicode glyphs.")

(defvar init-symbol-fonts '("Noto Emoji" "Symbola")
  "Fonts used for symbol/emoji faces.")

Setup symbol fonts.

(dolist (font init-symbol-fonts)
  (set-fontset-font t 'symbol font nil 'append))

Line length

(setq-default fill-column 90)

Underline

Underline line at descent position, not baseline position.

(setq x-underline-at-descent-line t)

Theme

Use a bar cursor by default.

(setq-default cursor-type 'bar)

Readable typography

(use-package readable-typo-theme
  :demand t
  :init
  (setq readable-typo-theme-default-font-height init-default-font-height)
  (setq readable-typo-theme-line-spacing init-line-spacing)
  (setq readable-typo-theme-fixed-pitch-font init-fixed-pitch-font)
  (setq readable-typo-theme-fixed-pitch-serif-font init-fixed-pitch-font)
  (setq readable-typo-theme-variable-pitch-font init-variable-pitch-font)
  (setq readable-typo-theme-serif-font init-serif-font)
  :config
  (load-theme 'readable-typo t))

Readable monochrome

(use-package readable-mono-theme
  :demand t
  :config
  (load-theme 'readable-mono t))
(defun toggle-theme-mode ()
  "Toggle dark/light `background-mode' and reload all loaded themes."
  (interactive)
  (customize-set-variable
   'frame-background-mode
   (if (eq 'light (frame-parameter nil 'background-mode)) 'dark 'light))
  (customize-set-variable 'custom-enabled-themes custom-enabled-themes))

Echo area

Attach a face to the echo area in order to style it differently.

(dolist (buffer-name '(" *Echo Area 0*"
                       " *Echo Area 1*"))
  (with-current-buffer (get-buffer-create buffer-name)
    (setq-local face-remapping-alist
                '((default readable-typo-theme-echo-area)))))

Layout

Frame

See Frame Layout documentation for details and terminology.

  • Add some padding around the whole window (internal-border-width) to provide some air.
  • Remove GUI elements.
  • Make the title-bar transparent on macOS.
(setq default-frame-alist
      '((undecorated . t)
        (internal-border-width . 16)
        (vertical-scroll-bars . nil)
        (menu-bar-lines . 0)
        (tool-bar-lines . 0)))

(when (eq system-type 'darwin)
  (setq frame-title-format nil)
  (dolist (filter '((ns-transparent-titlebar . t)
                    (ns-appearance . unbound)))
    (cl-pushnew filter default-frame-alist :test #'equal)))

Fringe

Setup fringes on both sides and display an indicator for buffer boundaries on the right side. Display fringes outside margins to have the padding on the inside.

(setq-default fringes-outside-margins t
              left-fringe-width 8
              right-fringe-width 8
              indicate-buffer-boundaries 'right)

Padding

Add margins inside windows to make text feel less crowded. Padding around the frame is configured via the internal-border-width in the Frame section.

(setq-default left-margin-width 1
              right-margin-width 1)

Borders

Add window dividers, mainly to add a border below the mode line.

(use-package frame
  :hook
  (server-after-make-frame-hook . window-divider-mode)
  :init
  (setq window-divider-default-places t)
  (setq window-divider-default-bottom-width 1)
  (setq window-divider-default-right-width 1))

Mode line

I try to retain functionality from the original mode line as much as possible, but I also want to simplify it slightly and separate into two parts; left side and right side mode line. There is a helper function in order to fill up the space needed to make the mode line appear on the right side. Also the coding system related information will be hidden if using utf-8-unix. I am also not displaying any minor mode lighters.

Define a variable to conveniently access only the major mode part of mode-line-modes.

(defvar mode-line-major-mode
  `(:propertize ("" mode-name)
                help-echo "Major mode\n\
mouse-1: Display major mode menu\n\
mouse-2: Show help for major mode\n\
mouse-3: Toggle minor modes"
                mouse-face mode-line-highlight
                local-map ,mode-line-major-mode-keymap))

Configure the order and components of the mode line.

(setq-default mode-line-format
              '("%e" mode-line-front-space
                mode-line-misc-info
                (vc-mode vc-mode)
                "  "
                mode-line-modified
                mode-line-remote
                mode-line-buffer-identification
                mode-line-position
                (:eval
                 (mode-line-right))
                mode-line-end-spaces))

Right alignment

Setup the right aligned mode line and helper functions to display it.

(defvar mode-line-right-format nil
  "The mode line to display on the right side.")

(defun mode-line-right ()
  "Render the `mode-line-right-format'."
  (let ((formatted-line (format-mode-line mode-line-right-format)))
    (list
     (propertize
      " "
      'display
      `(space :align-to (- right
                           (+ ,(string-width formatted-line) right-fringe right-margin))))
     formatted-line)))

Move default components to the right side of the mode line.

(setq mode-line-right-format
      (list '(:eval mode-line-mule-info)
            "  "
            mode-line-major-mode))

Position

Add position information including column and line number but skip the percentage.

(setq mode-line-position-column-line-format '(" L%l:C%C"))
(setq mode-line-percent-position nil)
(column-number-mode 1)
(line-number-mode 1)

Coding system

To reduce unnecessary information coding system will not be shown by default if the file is UTF-8 with UNIX end-of-line.

  • Only display “end of line”-mnemonic when not UNIX end-of-line.
  • Only display coding system when not UTF-8.
  • Other cases displays either with warning/error face in order to draw attention.
(setq eol-mnemonic-unix ""
      eol-mnemonic-dos (propertize "[CR+LF]" 'face 'warning)
      eol-mnemonic-mac (propertize "[CR]" 'face 'warning)
      eol-mnemonic-undecided (propertize "[?]" 'face 'error))

(let ((coding (nthcdr 2 mode-line-mule-info)))
  (setcar coding '(:eval (if (string-equal "U" (format-mode-line "%z"))
                             ""
                           (propertize "[%z]" 'face 'warning))))
  coding)

Indentation

Display information about the current indentation settings.

(use-package indent-info
  :ensure t
  :hook
  (after-init-hook . global-indent-info-mode)
  :init
  (setq indent-info-display-change-message-p nil)
  (setq indent-info-insert-target 'mode-line-mule-info)
  (setq indent-info-space-format "Spaces: %s")
  (setq indent-info-tab-format "Tab Size: %s")
  (setq indent-info-sync-from-editorconfig t)
  (setq indent-info-sync-to-editorconfig t))

Hide mode line

Support hiding the mode line, this can be useful for different modes displaying documents or presentation.

(use-package hide-mode-line
  :ensure t
  :commands
  (hide-mode-line-mode
   turn-on-hide-mode-line-mode
   turn-off-hide-mode-line-mode))

Icons

Modified

(defun mode-line-modified-icons ()
  "Icon representation of `mode-line-modified'."
  (cond (buffer-read-only
         (concat (all-the-icons-octicon "lock" :v-adjust -0.05) " "))
        ((buffer-modified-p)
         (concat (all-the-icons-faicon "floppy-o" :v-adjust -0.05) " "))
        ((and buffer-file-name
              (not (file-exists-p buffer-file-name)))
         (concat (all-the-icons-octicon "circle-slash" :v-adjust -0.05) " "))))

(setq-default mode-line-modified '((:eval (mode-line-modified-icons))))

Remote

(defun mode-line-remote-icons ()
  "Icon representation of `mode-line-remote'."
  (when (and buffer-file-name
             (file-remote-p buffer-file-name))
    (concat (all-the-icons-octicon "radio-tower" :v-adjust -0.02) " ")))

(setq-default mode-line-remote   '((:eval (mode-line-remote-icons))))

VCS

Shorten long Git branch names as well as replace Git prefix with a nice icon.

(defun vc-git-mode-line-shorten (string)
  "Shorten `version-control' STRING in mode-line and add icon."
  (cond
   ((string-prefix-p "Git" string)
    (concat (all-the-icons-octicon "git-branch" :v-adjust -0.05)
            " "
            (if (> (length string) 30)
                (concat (substring-no-properties string 4 30) "")
              (substring-no-properties string 4))))
   (t
    string)))
(advice-add 'vc-git-mode-line-string :filter-return #'vc-git-mode-line-shorten)

Keybindings

I am trying to reduce the amount of keybindings, therefore I present a table of default keybindings in case I would forget them.

Aliases

KeybindingAlternative
M-C-[
TABC-i
RETC-m

Commands

KeybindingFunctionDescription
C-oopen-lineOpen line below
C-jelectric-newline-and-maybe-indentAdd newline and indent if needed
M-SPCjust-one-spaceEnsures just one space
M-\delete-horizontal-spaceDelete all space
M-^delete-indentationJoin current line with previous line
M-zzap-to-charDelete until character
C-S-backspacekill-whole-lineKill entire lines, can be used to move several lines at once
M-/dabbrev-expandAbbreviation completion
M-tab / C-[ C-icompletion-at-pointMore context aware completion
C-wkill-regionCut
M-wkill-ring-saveCopy
C-yyankPaste
M-yyank-nextPaste (next item)
C-x SPCrectangle-mark-modeRectangular selection
C-x r tstring-rectangleInsert string in beginning of rectangular selection (C-t in rectangle-mark-mode)
C-M-fforward-sexpMove forward inside a balanced expression
C-M-bbackward-sexpMove backward inside a balanced expression
C-M-nforward-listMove forward across one balanced group of parenthesis
C-M-pbackward-listMove backward across one balanced group of parenthesis
M-mback-to-indentationMove to the first non-whitespace character
M-eforward-sentenceEnd of sentence
M-abackward-sentenceStart of sentence
C-M-ddown-listMove forward down one level of parenthesis
C-M-ubackward-up-listMove backward out of one level of parenthesis
C-u C-x $set-selective-displayHide/show indentation level

Gnus

KeybindingFunctionDescription
Lgnus-group-list-all-groupsList all groups (works together with prefix to provide level)
RETgnus-topic-select-groupList group mails (works together with prefix to see all)
ggnus-group-get-new-newsRefresh groups list
G Ggnus-group-make-nnir-groupSearch mails at server side
#gnus-group-mark-groupMark
M-#gnus-group-unmark-groupUn-mark
Rgnus-summary-reply-with-originalReply w/ quoted text
rgnus-summary-replyReploy w/o quoted text
S Wgnus-summary-wide-reply-with-originalReply all w/ quoted text
S wgnus-summary-wide-replyReply all w/o quoted text
m / C-x mgnus-new-mailCompose new mail
S D egnus-summary-resend-message-editRe-send a mail in Draft folder
C-c C-amml-attach-fileAttach a file
ognus-mime-save-partSave attachment
C-c C-fgnus-summary-mail-forwardForward mail
cgnus-group-catchup-currentMark mails as read

Org

KeybindingFunctionDescription
C-c C-worg-refileMove headline under another top level headline
<S-right>org-shiftrightCycle through todo keywords (right)
<S-left>org-shiftleftCycle through todo keywords (left)
C-c C-corg-ctrl-c-ctrl-cSet tags for an item
C-c C-torg-todoSet tags for an item (via menu)
C-c C-sorg-scheduleSchedule an item
C-c C-dorg-deadlineSet a deadline
C-c C-qorg-set-tags-commandAttach tags to item

Leader keys

(defvar leader-key "C-,"
  "The key used for most custom operations.")
(defvar local-leader-key "C-."
  "The key used for major mode operations.")

Prefixes

(defvar launch-prefix "C-c l"
  "Key prefix for commands related to launching.")

(defvar toggle-prefix "C-x t"
  "Key prefix for commands related to toggling.")
(defvar window-prefix "C-'"
  "Key prefix used for commands related to window operations.")

(defvar nav-prefix "M-g"
  "Key prefix used for commands related to navigation.")
(defvar search-prefix "M-s"
  "Key prefix used for commands related to search.")

(defvar next-prefix "M-]"
  "Key prefix used for commands doing a next operation.")
(defvar prev-prefix "M-["
  "Key prefix used for commands doing a previous operation.")

Packages

defrepeater

Library for defining repeating commands by repeating the last key.

(use-package defrepeater :ensure t :commands (defrepeater))

general

More convenient key definitions. It provides the use-package keyword :general.

(use-package general
  :ensure t
  :commands
  (general-define-key)
  :init
  (eval-when-compile
    (require 'general)))

which-key

Display available keybindings in a popup as you press keys.

(use-package which-key
  :ensure t
  :defer 3
  :commands
  (which-key-mode
   which-key-setup-side-window-bottom
   which-key-key-order-alpha)
  :init
  (setq which-key-sort-order #'which-key-key-order-alpha)
  (setq which-key-sort-uppercase-first nil)
  (setq which-key-add-column-padding 1)
  (setq which-key-min-display-lines 5)
  (setq which-key-idle-delay 1)
  :config
  (which-key-setup-side-window-bottom)
  (which-key-mode 1))

Global

(general-define-key
 :keymaps 'global
 ;; Editing
 "C-z" 'zap-up-to-char
 ;; Files
 "C-c o" 'ff-find-other-file
 "C-c O" 'ff-test-find-other-file
 ;; Region
 "C-x r S" 'sort-lines
 ;; Window
 "C-`" 'window-toggle-side-windows)

Navigation

(general-define-key
 :keymaps 'global
 :prefix nav-prefix
 "u" 'browse-url)

Window

(general-define-key
 :keymaps 'global
 :prefix window-prefix
 window-prefix 'window-toggle-side-windows
 "d" 'toggle-dedicated-window
 "m" 'maximize-window)

Next

(autoload 'smerge-next "smerge-mode")
(autoload 'flyspell-goto-next-error "flyspell")

(general-define-key
 :prefix next-prefix
 ""  '(:ignore t :wk "next...")
 "]" '(text-scale-increase      :wk "Text size")
 "b" '(next-buffer              :wk "Buffer")
 "c" '(smerge-next              :wk "Conflict")
 "e" '(next-error               :wk "Error")
 "s" '(flyspell-goto-next-error :wk "Spell error"))

(eval-and-compile
  (defrepeater #'text-scale-increase)
  (defrepeater #'next-buffer)
  (defrepeater #'smerge-next)
  (defrepeater #'next-error)
  (defrepeater #'flyspell-goto-next-error))

(general-define-key
 [remap text-scale-increase] 'text-scale-increase-repeat
 [remap next-buffer] 'next-buffer-repeat
 [remap smerge-next] 'smerge-next-repeat
 [remap next-error] 'next-error-repeat
 [remap flyspell-goto-next-error] 'flyspell-goto-next-error-repeat)

Previous

(autoload 'smerge-prev "smerge-mode")

(general-define-key
 :prefix prev-prefix
 ""  '(:ignore t :wk "previous...")
 "[" '(text-scale-decrease    :wk "Text size")
 "b" '(previous-buffer        :wk "Buffer")
 "c" '(smerge-prev            :wk "Conflict")
 "e" '(previous-error         :wk "Error"))

(eval-and-compile
  (defrepeater #'text-scale-decrease)
  (defrepeater #'previous-buffer)
  (defrepeater #'smerge-prev)
  (defrepeater #'previous-error))

(general-define-key
 [remap text-scale-decrease] 'text-scale-decrease
 [remap previous-buffer] 'previous-buffer
 [remap smerge-prev] 'smerge-prev
 [remap previous-error] 'previous-error)

Toggle

(general-define-key
 :prefix toggle-prefix
 "c" '(highlight-changes-mode           :wk "Changes")
 "d" '(toggle-debug-on-error            :wk "Debug on error")
 "f" '(hs-minor-mode                    :wk "Code folding")
 "g" '(glasses-mode                     :wk "Readable camelCase")
 "h" '(hl-line-mode                     :wk "Line highlight")
 "l" '(global-display-line-numbers-mode :wk "Line numbers")
 "L" '(so-long-mode                     :wk "Long lines")
 "s" '(subword-mode                     :wk "Sub-word")
 "t" '(toggle-truncate-lines            :wk "Truncate lines")
 "v" '(variable-pitch-mode              :wk "Variable-pitch")
 "w" '(whitespace-mode                  :wk "White-space")
 "x" '(flymake-mode                     :wk "Syntax checker"))

Launch

(general-define-key
 :prefix launch-prefix
 "m" 'gnus
 "p" 'list-processes
 "s" 'screenshot-svg
 "S" 'screenshot-png
 "x" 'regexp-builder
 "w" 'eww)

Completion

Enable indentation and completion with the TAB key.

(setq tab-always-indent 'complete)

Cycle with the TAB key if there are only few candidates.

(setq completion-cycle-threshold 3)

Style

orderless

Space-separated matching components matching in any order.

(use-package orderless
  :ensure t
  :init
  (setq completion-styles '(substring orderless))
  (setq completion-category-defaults nil)
  (setq completion-category-overrides '((file (styles partial-completion)))))

Expansion (hippie)

Smart expansion completions, excellent for completing lines. Replace abbrev completion (M-/) with hippie expand.

Complete in the following order:

  • Try to expand word “dynamically”, searching the current buffer.
  • Try to expand word “dynamically”, searching all other buffers.
  • Try to expand word “dynamically”, searching the kill ring.
  • Try to complete text as a file name, as many characters as unique.
  • Try to complete text as a file name.
  • Try to expand word before point according to all abbrev tables.
  • Try to complete the current line to an entire line in the buffer.
  • Try to complete as an Emacs Lisp symbol, as many characters as unique.
  • Try to complete word as an Emacs Lisp symbol.
(use-package hippie-exp
  :general
  ([remap dabbrev-expand] 'hippie-expand)
  :init
  (setq hippie-expand-try-functions-list
        '(try-expand-dabbrev-visible
          try-expand-dabbrev
          try-complete-file-name-partially
          try-complete-file-name
          try-expand-all-abbrevs
          try-expand-list
          try-expand-line
          try-expand-line-all-buffers
          try-complete-lisp-symbol-partially
          try-complete-lisp-symbol)))

Buffer

corfu

Completion Overlay Region FUnction.

(use-package corfu
  :ensure t
  :commands
  (corfu-mode)
  :general
  (:keymaps
   'corfu-map
   [return] 'nil
   "RET" 'nil
   "TAB" 'corfu-next
   [tab] 'corfu-next
   "S-TAB" 'corfu-previous
   [backtab] 'corfu-previous
   "C-e" 'corfu-insert)
  :init
  (setq corfu-auto t)
  (setq corfu-cycle t)
  (setq corfu-preselect-first nil)
  (corfu-mode 1))

corfu-doc

(use-package corfu-doc
  :ensure t
  :hook
  (corfu-mode-hook . corfu-doc-mode))

Minibuffer

vertico

Vertical interactive completion UI.

(use-package vertico
  :ensure t
  :hook
  (after-init-hook . vertico-mode)
  :commands
  (vertico-insert
   vertico-exit)
  :init
  (defun vertico-move-end-of-line-or-insert (arg)
    "Move to end of line or insert current candidate.
   ARG lines can be used.

   When only one candidate exists exit input after insert."
    (interactive "p")
    (if (eolp)
        (progn
          (vertico-insert)
          (when (= vertico--total 1)
            (vertico-exit)))
      (move-end-of-line arg)))
  :general
  (:keymaps
   'vertico-map
   "C-e" 'vertico-move-end-of-line-or-insert))

Extensions

all-the-icons-completion

(use-package all-the-icons-completion
  :ensure t
  :hook
  (marginalia-mode-hook . all-the-icons-completion-marginalia-setup))

consult

(use-package consult
  :ensure t
  :preface
  (autoload 'consult-xref "consult-xref")
  :general
  ([remap bookmark-jump] 'consult-bookmark
   [remap goto-line] 'consult-goto-line
   [remap switch-to-buffer] 'consult-buffer
   [remap imenu] 'consult-imenu
   [remap yank-pop] 'consult-yank-pop
   "C-c k" 'consult-kmacro
   "C-c r" 'consult-recent-file)
  (:prefix
   search-prefix
   "g" 'consult-ripgrep
   "m" 'consult-mark
   "M-m" 'consult-global-mark)
  :init
  (setq consult-preview-key (kbd "M-RET"))
  (eval-when-compile (require 'xref))
  (with-eval-after-load 'xref
    (setq xref-show-xrefs-function #'consult-xref)
    (setq xref-show-definitions-function #'consult-xref)))

marginalia

(use-package marginalia
  :ensure t
  :defer 2
  :commands
  (marginalia-mode)
  :config
  (marginalia-mode 1))

Help

(use-package help
  :general
  (:keymaps
   'help-map
   "B" 'find-library
   "u" 'describe-face
   "U" 'list-faces-display
   "'" 'describe-char))

(use-package help-at-pt
  :init
  (setq help-at-pt-timer-delay 0.1)
  (setq help-at-pt-display-when-idle '(flymake-diagnostic)))

ghelp

Generic help system.

(use-package ghelp
  :ensure t
  :general
  (:keymaps
   'help-map
   "A" 'ghelp-describe-elisp
   "f" 'ghelp-describe-function
   "k" 'ghelp-describe-key
   "v" 'ghelp-describe-variable)
  (:prefix
   nav-prefix
   "h" 'ghelp-describe-at-point))

helpful

Better *help* buffer.

(use-package helpful
  :ensure t
  :general
  (:keymaps
   'help-map
   "C" 'helpful-command
   "M" 'helpful-macro))

Display

Window

Favor horizontal splits

(setq split-width-threshold nil)

Resize pixelwise

(setq window-resize-pixelwise t)

Manage layouts

Undo/redo between window layouts.

(use-package winner
  :hook
  (window-setup-hook . winner-mode)
  :preface
  (defrepeater #'winner-redo)
  (defrepeater #'winner-undo)
  :general
  (:prefix
   next-prefix
   next-prefix '(winner-redo :wk "Window History"))
  (:prefix
   prev-prefix
   prev-prefix '(winner-undo :wk "Window History"))
  ([remap winner-redo] 'winner-redo-repeat
   [remap winner-undo] 'winner-undo-repeat)
  :init
  (setq winner-dont-bind-my-keys t))

Transpose window arrangement.

(use-package transpose-frame
  :ensure t
  :general
  (:prefix
   window-prefix
   "t" 'transpose-frame
   "f" 'flip-frame
   "F" 'flop-frame))

Zoom a window to display as a single window temporarily.

(use-package zoom-window
  :ensure t
  :general
  (:prefix
   window-prefix
   "z" 'zoom-window-zoom))

Buffer

Popups

Always display pop up buffers at the bottom and regard all star buffers as such buffers.

(setq switch-to-buffer-obey-display-actions t)
(dolist (rule `((,(rx bos "*" (one-or-more anything) "*" (optional "<" (one-or-more anything) ">") eos)
                 (display-buffer-reuse-window
                  display-buffer-in-side-window)
                 (reusable-frames . visible)
                 (side . bottom)
                 (window-height . 0.4))
                ("^*Warn about privacy*" display-buffer-pop-up-window)))
  (cl-pushnew rule display-buffer-alist :test #'equal))

Hide async shell command buffers

(cl-pushnew '("^*Async Shell Command*" . (display-buffer-no-window))
            display-buffer-alist
            :test #'equal)

Visual Fill Column

Wrap lines according to fill-column in visual-line-mode.

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

Editing

Indentation

Convert between tabs and spaces (only tabify initial white-space).

(setq-default tabify-regexp "^\t* [ \t]+")

Text flow

Wrap at words and don’t require double spaces to end a sentence.

(setq-default word-wrap t)

Kill-ring

Save clipboard contents into kill-ring before replacing them.

(setq save-interprogram-paste-before-kill t)

Parenthesis

Automatic parenthesis pairing and highlighting.

(electric-pair-mode 1)
(show-paren-mode 1)

Packages

cycle-quotes

(use-package cycle-quotes
  :ensure t
  :preface
  (defrepeater #'cycle-quotes)
  :general
  ("C-x C-'" 'cycle-quotes)
  ([remap cycle-quotes] 'cycle-quotes-repeat))

goto-addr

Buttonize URLs and e-mail addresses in the current buffer.

(use-package goto-addr
  :hook
  (text-mode-hook . goto-address-mode)
  (prog-mode-hook . goto-address-prog-mode))

grugru

Cycle through words, symbols and patterns.

(use-package grugru
  :ensure t
  :commands
  (grugru-default-setup
   grugru-define-global
   grugru-define-on-major-mode)
  :preface
  (eval-when-compile
    (require 'grugru-default))
  (defrepeater #'grugru-backward)
  (defrepeater #'grugru-forward)
  :general
  (:prefix
   next-prefix
   "r" '(grugru-forward :wk "Rotate text"))
  (:prefix
   prev-prefix
   "r" '(grugru-backward :wk "Rotate text"))
  ([remap grugru-backward] 'grugru-backward-repeat
   [remap grugru-forward] 'grugru-forward-repeat)
  :config
  (grugru-default-setup)
  (grugru-define-global 'symbol '("assert" "refute"))
  (grugru-define-global 'symbol '("yes" "no"))
  (grugru-define-on-major-mode 'nix-mode 'symbol '("true" "false")))

puni

Soft deletion and balanced expressions.

(use-package puni
  :ensure t
  :commands
  (puni-kill-line
   puni-kill-active-region)
  :preface
  (defun puni-kill-whole-line-or-region (arg)
    "Kill whole line(s) based on ARG or active region."
    (interactive "P")
    (if (use-region-p)
        (puni-kill-active-region)
      (if (or (> (or arg 1) 1)
              (looking-at-p "^[[:blank:]]*$"))
          (save-excursion
            (set-mark (line-beginning-position 1))
            (goto-char (line-beginning-position (+ 1 (or arg 1))))
            (puni-kill-active-region))
        (save-excursion
          (beginning-of-line)
          (puni-kill-line 1)))))
  :hook
  (after-init-hook . puni-global-mode)
  :general
  (:prefix
   toggle-prefix
   "p" 'puni-mode)
  (:keymaps
   'puni-mode-map
   "C-w" 'puni-kill-whole-line-or-region))

string-inflection

Conversion between different variable naming conventions. Toggle between snake/pascal/camel/up/kebab-case or capital underscore.

(use-package string-inflection
  :ensure t
  :general
  ("M-_" 'string-inflection-all-cycle))

smartparens

Auto-insert matching parenthesis and highlight matching parenthesis.

(use-package smartparens
  :ensure t
  :general
  ("M-'" 'sp-change-inner
   "M-C" 'sp-clone-sexp
   "M-D" 'sp-unwrap-sexp
   "M-R" 'sp-rewrap-sexp
   "M-W" 'sp-wrap-round))

visual-regexp

Visually highlight regular expression searches as you type. Also supports replace.

(use-package visual-regexp
  :ensure t
  :general
  ("M-s r" 'vr/query-replace
   "M-s R" 'vr/replace))

whitespace

Display whitespace

(use-package whitespace
  :init
  (setq whitespace-line-column fill-column)
  (setq whitespace-style
        '(face tabs tab-mark spaces space-mark trailing lines-tail))
  (setq whitespace-display-mappings
        '((tab-mark 9 [8250 9])
          (newline-mark 10 [172 10])
          (space-mark 32 [183] [46]))))

whole-line-or-region

Cut/copy (C-w / M-w) the current line if no region is active.

(use-package whole-line-or-region
  :ensure t
  :hook
  (after-init-hook . whole-line-or-region-global-mode))

yasnippet

(use-package yasnippet
  :ensure t
  :hook
  (after-init-hook . yas-global-mode)
  :general
  (:keymaps
   'yas-minor-mode-map
   [tab] 'nil
   "TAB" 'nil
   "M-o" 'yas-insert-snippet)
  :init
  (setq yas-verbosity 0)
  (setq yas-also-auto-indent-first-line t)
  (setq yas-snippet-dirs
        (list (expand-file-name "snippets" user-emacs-directory)))
  ;; Nested snippets
  (setq yas-triggers-in-field t)
  (setq yas-wrap-around-region t))

(use-package yasnippet-snippets
  :ensure t
  :hook
  (yas-minor-mode-hook . yasnippet-snippets-initialize))

yatemplate

(use-package autoinsert
  :hook
  (after-init-hook . auto-insert-mode)
  :init
  (setq auto-insert-query nil))

(use-package yatemplate
  :ensure t
  :hook
  (auto-insert-mode-hook . yatemplate-fill-alist))

Appearance

Line highlight

Disabled by default. When enabled, only highlight in the selected window.

(use-package hl-line
  :init
  (setq hl-line-sticky-flag nil)
  (setq global-hl-line-sticky-flag nil))

Page breaks

Display page breaks as a horizontal line.

(use-package page-break-lines
  :ensure t
  :defer 3
  :commands
  (global-page-break-lines-mode)
  :config
  (global-page-break-lines-mode 1))

Visible

Making invisible text temporarily visible.

(use-package simple
  :general
  (:prefix
   local-leader-key
   "v" 'visible-mode))

Tables

(use-package valign
  :ensure t
  :hook
  (markdown-mode-hook . valign-mode)
  (org-mode-hook . valign-mode)
  :init
  (setq valign-fancy-bar t))

Buffers

Prevent generation of useless lock and backup files.

(setq create-lockfiles nil)
(setq make-backup-files nil)

Don’t require confirmation when opening a new buffer.

(setq confirm-nonexistent-file-or-buffer t)

Remove visual indicators from non-selected windows

(setq highlight-nonselected-windows nil)
(setq-default cursor-in-non-selected-windows nil)

Auto save

(use-package files
  :hook
  (after-init-hook . auto-save-visited-mode)
  :init
  (setq auto-save-no-message t)
  (setq save-abbrevs 'silently))

Auto executable scripts

(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)

Minibuffer

Enable recursive minibuffers.

(setq enable-recursive-minibuffers t)

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)

Give some more room to the minbuffer.

(setq max-mini-window-height 0.3)
(setq resize-mini-windows 'grow-only)

No fringes

Don’t show fringes in the minibuffer.

(defun disable-minibuffer-window-fringes (&rest _)
  "Disable the window fringes for minibuffer window."
  (set-window-fringes (minibuffer-window) 0 0 nil))
(add-hook 'minibuffer-setup-hook #'disable-minibuffer-window-fringes)

History

Track minibuffer history

(setq history-delete-duplicates t)
(setq history-length 500)

embark

(use-package embark
  :ensure t
  :general
  (:keymaps
   '(minibuffer-local-map
     minibuffer-local-ns-map
     minibuffer-local-completion-map
     minibuffer-local-must-match-map)
   "C-." 'embark-act
   "C-;" 'embark-dwim))

(use-package embark-consult :ensure t)

Miniedit

Edit minibuffer in a new temporary buffer by pressing =C-c ‘=.

(use-package miniedit
  :ensure t
  :general
  (:keymaps
   '(minibuffer-local-map
     minibuffer-local-ns-map
     minibuffer-local-completion-map
     minibuffer-local-must-match-map)
   "C-c '" 'miniedit)
  :init
  (setq miniedit-show-help-p nil))

Scrolling

More procedural scrolling.

(setq auto-window-vscroll nil)
(setq hscroll-margin 5)
(setq hscroll-step 5)
(setq scroll-margin 0)
(setq scroll-preserve-screen-position t)

(setq-default scroll-down-aggressively 0.01)
(setq-default scroll-up-aggressively 0.01)

Packages

autorevert

Revert buffers when underlying files change.

(use-package autorevert
  :init
  (setq auto-revert-verbose nil))

default-text-scale

(use-package default-text-scale
  :ensure t
  :general
  ("C-M-=" 'default-text-scale-increase)
  ("C-M--" 'default-text-scale-decrease)
  ("C-M-0" 'default-text-scale-reset))

eldoc

(use-package eldoc
  :hook
  (after-init-hook . global-eldoc-mode))

rainbow-mode

Display colors inline.

(use-package rainbow-mode
  :ensure t
  :minor
  "-theme\\.el\\'"
  :hook
  (help-mode-hook . rainbow-mode))

readable

(use-package readable
  :hook
  ((eww-mode-hook
    Info-mode-hook
    markdown-mode-hook
    nov-mode-hook
    org-mode-hook
    outline-mode-hook
    rst-mode-hook) . readable-mode))

relative-buffers

(use-package relative-buffers
  :ensure t
  :hook
  (after-init-hook . global-relative-buffers-mode)
  :init
  (setq relative-buffers-project-prefix t))

wgrep-ag

Writeable grep buffer with ability to apply the changes to all the files.

(use-package wgrep-ag
  :ensure t
  :init
  (setq wgrep-auto-save-buffer t))

Navigation

Allow repeated mark popping. This behavior is similar to Vim’s C-o. With this configuration you can press C-u and continuously C-SPC to jump to previous entries in the mark ring.

(setq set-mark-command-repeat-pop t)

Dired

(use-package dired
  :hook
  (dired-mode-hook . auto-revert-mode)
  (dired-mode-hook . hl-line-mode)
  (dired-mode-hook . dired-hide-details-mode)
  :init
  (setq dired-listing-switches "-al --group-directories-first")
  ;; Always copy/delete recursively
  (setq dired-recursive-copies  'always)
  (setq dired-recursive-deletes 'top))

Editable

(use-package wdired
  :general
  (:keymaps
   'dired-mode-map
   "C-c '" 'wdired-change-to-wdired-mode))

Search

(use-package find-dired
  :general
  ("C-x D" 'find-dired)
  :init
  (setq find-ls-option '("-print0 | xargs -0 ls -ld" . "-ld")))

Sidebar

(use-package dired-sidebar
  :ensure t
  :preface
  (defun init-dired-sidebar ()
    (setq cursor-type nil)
    (stripe-buffer-mode 0))
  :hook
  (dired-sidebar-mode-hook . hide-mode-line-mode)
  (dired-sidebar-mode-hook . hl-line-mode)
  (dired-sidebar-mode-hook . variable-pitch-mode)
  (dired-sidebar-mode-hook . init-dired-sidebar)
  :general
  (:prefix
   leader-key
   "n" 'dired-sidebar-toggle-sidebar))

Subtree

Display subtrees in dired view.

(use-package dired-subtree
  :ensure t
  :init
  (setq dired-subtree-use-backgrounds nil)
  (setq dired-subtree-line-prefix "     "))

Stripes

Striped dired buffers.

(use-package stripe-buffer
  :ensure t
  :hook
  (dired-mode-hook . stripe-buffer-mode))

Icons

(use-package all-the-icons-dired
  :ensure t
  :hook
  (dired-mode-hook . all-the-icons-dired-mode))

Git

(use-package dired-git-info
  :ensure t
  :general
  (:keymaps
   'dired-mode-map
   ")" 'dired-git-info-mode))

Occur

(use-package replace
  :general
  (:prefix
   search-prefix
   "o" 'occur)
  (:keymaps
   'occur-mode-map
   "C-c '" 'occur-edit-mode))

Packages

noccur

(use-package noccur
  :ensure t
  :general
  (:prefix
   search-prefix
   "O" 'noccur-project))

Packages

bibliothek

Management tool for a library of PDFs.

(use-package bibliothek
  :ensure t
  :general
  (:prefix
   launch-prefix
   "b" 'bibliothek)
  :init
  (setq bibliothek-path '("~/books" "~/documents/research/papers"))
  (setq bibliothek-recursive t))

bookmark

Keep track of bookmarks

(use-package bookmark
  :init
  (setq bookmark-save-flag 1))

ctrlf

(use-package ctrlf
  :ensure t
  :hook
  (after-init-hook . ctrlf-mode))

ctrlxo

(use-package ctrlxo
  :ensure t
  :general
  ("C-x o" 'ctrlxo))

deadgrep

(use-package deadgrep
  :ensure t
  :general
  (:prefix search-prefix "G" 'deadgrep)
  (:keymaps
   'deadgrep-mode-map
   "C-c '" 'deadgrep-edit-mode)
  (:keymaps
   'deadgrep-edit-mode-map
   "C-c C-c" 'deadgrep-mode))

find-file

(use-package find-file
  :init
  (setq-default ff-quiet-mode t)
  (put 'ff-search-directories
       'safe-local-variable
       (lambda (x) (cl-every #'stringp x))))

(use-package ff-test :demand t)

find-file-rg

Find files via rg --files.

(use-package find-file-rg
  :ensure t
  :general
  ("C-c f" 'find-file-rg))

goto-last-change

Move point through buffer-undo-list positions.

(use-package goto-chg
  :ensure t
  :general
  (:prefix
   next-prefix
   "l" '(goto-last-change :wk "Change"))
  (:prefix
   prev-prefix
   "l" '(goto-last-change-reverse :wk "Change")))

link-hint

Hint mode for links.

(use-package link-hint
  :ensure t
  :general
  (:prefix nav-prefix
   "l" 'link-hint-open-link
   "L" 'link-hint-copy-link))

recentf

Keep track of recently opened files.

(use-package recentf
  :defer 1
  :init
  (setq recentf-exclude
        (list "/tmp/"                        ; Temp-files
              "/dev/shm"                     ; Potential secrets
              "/ssh:"                        ; Files over SSH
              "/nix/store"                   ; Files in Nix store
              "/TAGS$"                       ; Tag files
              "^/\\.git/.+$"                 ; Git contents
              "\\.?ido\\.last$"
              "\\.revive$"
              "^/var/folders/.+$"
              (concat "^" cache-dir ".+$")
              (concat "^" data-dir ".+$")))
  (setq recentf-filename-handlers '(abbreviate-file-name))
  (setq recentf-max-menu-items 0)
  (setq recentf-max-saved-items 300)
  (setq recentf-auto-cleanup 'never)
  :config
  (quiet! (recentf-mode 1)))

saveplace

Keep track of last point place to resume editing in the same file.

(use-package saveplace
  :defer 2
  :config
  (save-place-mode 1))

Project

Retrieve project list via ghq.

(use-package project
  :preface
  (defun project--read-project-list-from-ghq ()
    "Initialize `project--list' using contents of command ghq output."
    (with-temp-buffer
      (call-process "ghq" nil t nil "list" "--full-path")
      (goto-char (point-min))
      (while (not (eobp))
        (cl-pushnew
         (list (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
         project--list
         :test #'equal)
        (forward-line 1))))
  :init
  (advice-add 'project--read-project-list :after #'project--read-project-list-from-ghq))

Development

Reduce scroll margin.

(defun prog-scroll-margin-setup ()
  "Setup `scroll-margin' for `prog-mode'."
  (setq-local scroll-margin 3))
(add-hook 'prog-mode-hook #'prog-scroll-margin-setup)

Compilation

Kill compilation process before stating another and save all buffers on compile.

(use-package compile
  :general
  (:keymaps
   'global
   :prefix
   local-leader-key
   "c" 'recompile
   "C" 'compile)
  :init
  (setq compilation-always-kill t)
  (setq compilation-ask-about-save nil)
  (setq compilation-scroll-output t))

(make-variable-buffer-local 'compile-command)
(put 'compile-command 'safe-local-variable 'stringp)

ANSI escape

(use-package ansi-color
  :preface
  (autoload 'ansi-color-apply-on-region "ansi-color")
  (defun ansi-color-colorize-compilation ()
    "Colorize from `compilation-filter-start' to `point'."
    (let ((inhibit-read-only t))
      (ansi-color-apply-on-region compilation-filter-start (point))))
  :hook
  (compilation-filter-hook . ansi-color-colorize-compilation))

Containers

Docker

(use-package docker
  :ensure t
  :general
  (:prefix
   leader-key
   "d" 'docker
   "c" 'docker-compose))

Files

Support for Docker related files.

(use-package dockerfile-mode :ensure t)
(use-package docker-compose-mode :ensure t)

Tramp

(use-package docker-tramp
  :ensure t
  :init
  (setq docker-tramp-use-names t))

Kubernetes

(use-package kubernetes :ensure t)

Tramp

(use-package kubernetes-tramp :ensure t)

Coverage

(use-package coverlay
  :ensure t
  :commands
  (coverlay-minor-mode
   coverlay-watch-file)
  :preface
  (defun init-coverlay ()
    "Setup `coverlay-mode'."
    (setq-local
     coverlay:base-path
     (expand-file-name (locate-dominating-file (file-name-directory (buffer-file-name))
                                               "coverage"))))

  (defun coverlay-mode-toggle ()
    "Turn on `coverlay-mode'."
    (interactive)
    (if (bound-and-true-p coverlay-minor-mode)
        (coverlay-minor-mode 0)
      (coverlay-minor-mode 1)
      (when (and (buffer-file-name) (not (bound-and-true-p coverlay--loaded-filepath)))
        (let ((coverage-file (expand-file-name "coverage/lcov.info" coverlay:base-path)))
          (when (file-exists-p coverage-file)
            (coverlay-watch-file coverage-file))))))
  :hook
  (coverlay-minor-mode-hook . init-coverlay)
  :general
  (:prefix
   local-leader-key
   "v" 'coverlay-mode-toggle)
  :init
  (setq coverlay:mark-tested-lines nil))

Docs

DevDocs

Lookup documentation via DevDocs.

(use-package devdocs
  :ensure t
  :general
  (:prefix nav-prefix "K" 'devdocs-search))

Editing

separedit

(use-package separedit
  :ensure t
  :general
  (:keymaps
   '(prog-mode-map typescript-mode-map)
   "C-c '" 'separedit)
  :init
  (setq separedit-preserve-string-indentation t))

Eval

quickrun

(use-package quickrun
  :ensure t
  :hook
  (quickrun--mode-hook . display-ctrl-M-as-newline)
  :general
  (:prefix
   local-leader-key
   "q" 'quickrun
   "Q" 'quickrun-autorun-mode))

Folding

Code folding.

(use-package hideshow
  :preface
  (defun hs-fold-overlay-ellipsis (ov)
    (when (eq 'code (overlay-get ov 'hs))
      (overlay-put
       ov 'display (propertize "" 'face 'font-lock-comment-face))))
  :hook
  (prog-mode-hook . hs-minor-mode)
  :general
  (:prefix
   (concat leader-key " " "z")
   ""      '(:ignore t :wk "hide")
   "c"     'hs-hide-block
   "o"     'hs-show-block
   "C"     'hs-hide-all
   "O"     'hs-show-all
   "l"     'hs-hide-level
   "z"     'hs-toggle-hiding
   "<tab>" 'hs-toggle-hiding)
  :init
  (setq hs-hide-comments-when-hiding-all nil)
  (setq hs-allow-nesting t)
  (setq hs-set-up-overlay #'hs-fold-overlay-ellipsis))

Formatting

apheleia

Reformat buffer without moving point.

(use-package apheleia
  :ensure t
  :hook
  (prog-mode-hook . apheleia-mode)
  :general
  (:keymaps
   'prog-mode-map
   :prefix local-leader-key
   "f" 'apheleia-format-buffer)
  :init
  (put 'apheleia-formatter 'safe-local-variable 'symbolp)
  :config
  (progn ;; JavaScript/TypeScript
    (cl-pushnew '(eslint . (npx "eslint_d" "--fix-to-stdout" "--stdin" "--stdin-filename" file)) apheleia-formatters :test #'equal))
  (progn ;; Nix
    (cl-pushnew '(nix . ("nix" "fmt" "--" "-")) apheleia-formatters :test #'equal)
    (cl-pushnew '(nix-mode . nix) apheleia-mode-alist :test #'equal))
  (progn ;; Ruby
    (cl-pushnew '(rufo . ("rufo" "--simple-exit")) apheleia-formatters :test #'equal)
    (cl-pushnew '(ruby-mode . rufo) apheleia-mode-alist :test #'equal)))

Jump to definition

Jump to definition is really useful and I prefer doing so without TAGS which is pretty much the default for most modes. I am using the excellent package dumb-jump to jump via grep tools e.g. (grep, rx, ag)

Don’t ask about keeping current list of tags tables.

(use-package etags
  :init
  (setq tags-add-tables nil))

xref

(use-package xref
  :commands
  (xref-show-definitions-completing-read)
  :general
  (:prefix
   nav-prefix
   "i" 'xref-find-definitions
   "o" 'xref-find-definitions-other-window)
  :config
  (remove-hook 'xref-backend-functions #'etags--xref-backend))

dumb-jump

(use-package dumb-jump
  :ensure t
  :hook
  (xref-backend-functions . dumb-jump-xref-activate)
  :init
  (setq dumb-jump-default-project user-emacs-directory)
  (setq dumb-jump-selector 'completing-read))

Refactor

Refactoring commands for various languages.

(use-package emr
  :ensure t
  :general
  (:keymaps
   'prog-mode-map
   "M-<return>" 'emr-show-refactor-menu))

REPL

comint

(use-package comint
  :commands
  (comint-send-string)
  :preface
  (defun comint-process-tab-complete ()
    "Complete by sending the current input and TAB character to the process."
    (interactive)
    (let* ((buffer (current-buffer))
           (pmark (process-mark (get-buffer-process buffer))))
      (when (> (point) (marker-position pmark))
        (kill-region pmark (point))
        (comint-send-string buffer (concat (pop kill-ring) "\t")))))
  :general
  (:keymaps
   'comint-mode-map
   "<tab>" 'comint-process-tab-complete
   "C-c C-k" 'term-char-mode)
  :init
  (setq comint-use-prompt-regexp t))

repl-toggle

(use-package repl-toggle
  :ensure t
  :preface
  (defun clojure-repl ()
    "Open a Clojure REPL."
    (interactive)
    (pop-to-buffer (cider-current-repl nil 'ensure)))

  (defun js-repl ()
    "Open a JavaScript REPL."
    (interactive)
    (if (indium-client-process-live-p) (indium-switch-to-repl-buffer) (nodejs-repl)))

  (defun lua-repl ()
    "Open a Lua REPL."
    (interactive)
    (pop-to-buffer (process-buffer (lua-get-create-process))))
  :general
  (:keymaps
   'prog-mode-map
   :prefix local-leader-key
   "r" 'rtog/toggle-repl)
  :init
  (setq rtog/goto-buffer-fun 'pop-to-buffer)
  (setq rtog/mode-repl-alist
        '((emacs-lisp-mode . ielm)
          (clojure-mode . clojure-repl)
          (elm-mode . elm-repl-load)
          (go-mode . gorepl-run)
          (js-mode . js-repl)
          (lua-mode . lua-repl)
          (nix-mode . nix-repl)
          (racket-mode . racket-repl)
          (typescript-mode . run-ts))))

Persistent history in comint

(autoload 'comint-read-input-ring "comint")
(autoload 'comint-write-input-ring "comint")

(defun +comint-history-write-on-exit (process event)
  "Write `comint' history on exit.
Receives PROCESS and EVENT."
  (comint-write-input-ring)
  (let ((buf (process-buffer process)))
    (when (buffer-live-p buf)
      (with-current-buffer buf
        (insert (format "\nProcess %s %s" process event))))))

(defun +comint-history-enable ()
  "Enable `comint' history."
  (let ((process (get-buffer-process (current-buffer))))
    (when process
      (setq comint-input-ring-file-name
            (expand-file-name
             (format "comint-%s-history" (process-name process))
             cache-dir))
      (comint-read-input-ring)
      (set-process-sentinel process #'+comint-history-write-on-exit))))

Syntax checker

Silence next/previous error, by default it produces a message every time.

(advice-add 'next-error :around #'quiet-function-advice)
(advice-add 'previous-error :around #'quiet-function-advice)

flymake

Connect flymake to next-error-function and add some navigation bindings. Disable the legacy diagnostic functions as some of them have bugs and cause instability (mainly the Haskell one).

(use-package flymake
  :preface
  (defun flymake-setup-next-error-function ()
    (setq next-error-function 'flymake-next-error-compat))

  (defun flymake-next-error-compat (&optional n _)
    (flymake-goto-next-error n))

  (defun flymake-diagnostics-next-error ()
    (interactive)
    (forward-line)
    (when (eobp) (forward-line -1))
    (flymake-show-diagnostic (point)))

  (defun flymake-diagnostics-prev-error ()
    (interactive)
    (forward-line -1)
    (flymake-show-diagnostic (point)))
  :hook
  (flymake-mode-hook . flymake-setup-next-error-function)
  :general
  (:keymaps
   'flymake-mode-map
   :prefix
   local-leader-key
   "!" 'flymake-show-buffer-diagnostics)
  (:keymaps
   'flymake-mode-map
   :prefix next-prefix
   "E" 'flymake-goto-next-error)
  (:keymaps
   'flymake-mode-map
   :prefix prev-prefix
   "E" 'flymake-goto-prev-error)
  (:keymaps
   'flymake-diagnostics-buffer-mode-map
   "n" 'flymake-diagnostics-next-error
   "p" 'flymake-diagnostics-prev-error
   "j" 'flymake-diagnostics-next-error
   "k" 'flymake-diagnostics-prev-error
   "RET" 'flymake-goto-diagnostic
   "TAB" 'flymake-show-diagnostic)
  :init
  (setq flymake-proc-ignored-file-name-regexps '("\\.l?hs\\'"))

  (remove-hook 'flymake-diagnostic-functions 'flymake-proc-legacy-flymake))

Diagnostics at point

(use-package flymake-diagnostic-at-point
  :ensure t
  :preface
  (defun flymake-diagnostic-at-point-quick-peek (text)
    "Display the flymake diagnostic TEXT with `quick-peek'`."
    (quick-peek-show (concat flymake-diagnostic-at-point-error-prefix text)))
  :hook
  (flymake-mode-hook . flymake-diagnostic-at-point-mode)
  :init
  (setq flymake-diagnostic-at-point-error-prefix nil))

Version control

(setq vc-follow-symlinks t)
(setq vc-make-backup-files nil)

browse-at-remote

Open link to files in the web UI connected to a repository.

(use-package browse-at-remote
  :ensure t
  :general
  ("C-x v SPC" 'browse-at-remote))

diff

(use-package diff
  :preface
  (defrepeater #'diff-hunk-next)
  (defrepeater #'diff-hunk-prev)
  :general
  (:keymaps
   'diff-mode-map
   :prefix next-prefix
   "d" '(diff-hunk-next :wk "Diff Hunk"))
  (:keymaps
   'diff-mode-map
   :prefix prev-prefix
   "d" '(diff-hunk-prev :wk "Diff Hunk"))
  ([remap diff-hunk-next] 'diff-hunk-next-repeat
   [remap diff-hunk-prev] 'diff-hunk-prev-repeat)
  :init
  (setq diff-font-lock-prettify t))

ediff

  • Split horizontally
  • Use existing frame instead of creating a new one
  • Add a third resolution option, copy both A and B to C
(use-package ediff
  :preface
  (autoload 'ediff-copy-diff "ediff-util")
  (autoload 'ediff-get-region-contents "ediff-util")
  (autoload 'ediff-setup-windows-plain "ediff-wind")

  (defun ediff-copy-both-to-C ()
    "Copy change from both A and B to C."
    (interactive)
    (ediff-copy-diff
     ediff-current-difference nil 'C nil
     (concat
      (ediff-get-region-contents ediff-current-difference 'A ediff-control-buffer)
      (ediff-get-region-contents ediff-current-difference 'B ediff-control-buffer))))

  (defun init-ediff-keys ()
    "Setup keybindings for `ediff-mode'."
    (general-define-key
     :keymaps 'ediff-mode-map
     "d" '(ediff-copy-both-to-C      :wk "Copy both to C")
     "j" '(ediff-next-difference     :wk "Next difference")
     "k" '(ediff-previous-difference :wk "Previous difference")))
  :hook
  (ediff-quit-hook . winner-undo)
  (ediff-keymap-setup-hook . init-ediff-keys)
  :init
  (setq ediff-diff-options "-w")
  (setq ediff-merge-split-window-function #'split-window-horizontally)
  (setq ediff-split-window-function #'split-window-horizontally)
  (setq ediff-window-setup-function #'ediff-setup-windows-plain))

diff-hl

Diff indicators in fringe

(use-package diff-hl
  :ensure t
  :defer 2
  :commands
  (global-diff-hl-mode)
  :preface
  (autoload 'diff-hl-flydiff-mode "diff-hl-flydiff" nil t)
  (autoload 'diff-hl-dired-mode "diff-hl-dired" nil t)
  (defrepeater #'diff-hl-next-hunk)
  (defrepeater #'diff-hl-previous-hunk)
  :hook
  (dired-mode-hook . diff-hl-dired-mode)
  (magit-post-refresh-hook . diff-hl-magit-post-refresh)
  :general
  (:keymaps
   'diff-hl-mode-map
   :prefix next-prefix
   "d" '(diff-hl-next-hunk :wk "Diff Hunk"))
  (:keymaps
   'diff-hl-mode-map
   :prefix prev-prefix
   "d" '(diff-hl-previous-hunk :wk "Diff Hunk"))
  ([remap diff-hl-next-hunk] 'diff-hl-next-hunk-repeat
   [remap diff-hl-previous-hunk] 'diff-hl-previous-hunk-repeat)
  :init
  (setq diff-hl-ask-before-revert-hunk nil)
  :config
  (global-diff-hl-mode 1)
  (diff-hl-flydiff-mode 1))

magit

Enhanced git related views and commands.

(use-package magit
  :ensure t
  :defer 2
  :preface
  (eval-when-compile
    (require 'vc-msg-git))
  (defun +git-commit-set-fill-column ()
    "Set `fill-column' for git commit."
    (setq fill-column 72))

  (defvar magit-process-create-pull-request-regexp
    "remote: Create pull request for.*\nremote: +\\(?1:[^ ]+\\)[^\n]*"
    "Regular expression detecting PR.")

  (defun magit-process-ask-create-pull-request (_process string)
    "Detect pull request STRING and ask to create PR."
    (when (string-match magit-process-create-pull-request-regexp string)
      (let ((url (match-string 1 string))
            (inhibit-message t))
        (if (y-or-n-p "Create PR? ")
            (browse-url (url-encode-url url))))))
  :hook
  (git-commit-mode-hook . +git-commit-set-fill-column)
  :general
  (:keymaps
   'dired-mode-map
   "C-x g" 'magit)
  :init
  (setq magit-log-buffer-file-locked t)
  (setq magit-refs-show-commit-count 'all)
  (setq magit-save-repository-buffers 'dontask)
  (setq magit-process-prompt-functions #'magit-process-ask-create-pull-request)
  :config
  ;; Unset pager as it is not supported properly inside emacs.
  (setenv "GIT_PAGER" ""))

vc-msg

Popup commit message for current line

(use-package vc-msg
  :ensure t
  :general
  ("C-x v p" 'vc-msg-show))

Packages

envrc

Project-specific environment variables via direnv.

(use-package envrc
  :ensure t
  :if (executable-find "direnv")
  :defer 1
  :commands
  (envrc-global-mode)
  :general
  (:keymaps
   'envrc-mode-map
   "C-c e" 'envrc-command-map)
  :config
  (envrc-global-mode 1))

editorconfig

Use EditorConfig to maintain the coding styles used across different projects.

(use-package editorconfig
  :ensure t
  :defer 1
  :commands
  (editorconfig-mode)
  :init
  (setq editorconfig-trim-whitespaces-mode 'ws-butler-mode)
  (setq editorconfig-exclude-modes '(emacs-lisp-mode
                                     lisp-mode
                                     org-mode))
  :config
  (cl-pushnew '(plantuml-mode plantuml-indent-level) editorconfig-indentation-alist :test #'equal)
  (editorconfig-mode 1))

eglot

Generic Language Server Protocol integration via eglot.

(use-package eglot
  :ensure t
  :defer 3
  :preface
  (defun +eglot-ensure-unless-json-mode ()
    (unless (derived-mode-p 'json-mode) (eglot-ensure)))
  :hook
  ((haskell-mode-hook
    java-mode-hook
    ruby-mode-hook
    rustic-mode-hook
    typescript-mode-hook) . eglot-ensure)
  (js-mode-hook . +eglot-ensure-unless-json-mode)
  :general
  (:keymaps
   'eglot-mode-map
   :prefix nav-prefix
   "k" 'eglot-help-at-point)
  (:keymaps
   'eglot-mode-map
   :prefix local-leader-key
   "e" '(:ignore t :wk "eglot")
   "ea" 'eglot-code-actions
   "ef" 'eglot-format
   "eh" 'eglot-help-at-point
   "er" 'eglot-rename)
  :init
  (setq eglot-autoshutdown t)
  (setq eglot-confirm-server-initiated-edits nil)
  (setq eglot-sync-connect nil))

hl-todo

Highlight TODO inside comments and strings.

(use-package hl-todo
  :ensure t
  :preface
  (defrepeater #'hl-todo-next)
  (defrepeater #'hl-todo-previous)
  :hook
  (prog-mode-hook . hl-todo-mode)
  :general
  (:prefix next-prefix "t" '(hl-todo-next :wk "Todo"))
  (:prefix prev-prefix "t" '(hl-todo-previous :wk "Todo"))
  ([remap hl-todo-next] 'hl-todo-next-repeat
   [remap hl-todo-previous] 'hl-todo-previous-repeat))

idle-highlight

Highlight symbol at point on idle.

(use-package idle-highlight-mode
  :ensure t
  :hook
  (prog-mode-hook . idle-highlight-mode))

imenu-anywhere

Jump to document locations in current buffer

(use-package imenu-anywhere
  :ensure t
  :general
  (:prefix
   search-prefix
   "i" 'imenu
   "I" 'imenu-anywhere))

imenu-extra

Easily add document locations via regular expressions.

(use-package imenu-extra
  :ensure t
  :functions (imenu-extra-auto-setup))

imenu-list

Document locations in a sidebar.

(use-package imenu-list
  :ensure t
  :general
  (:prefix
   leader-key
   "i" 'imenu-list-smart-toggle))

source-peek

Peek definition (Display the function source inline).

(use-package source-peek
  :ensure t
  :general
  (:prefix
   nav-prefix
   "SPC" 'source-peek))

ws-butler

Delete trailing white-space before save, but only for edited lines.

(use-package ws-butler
  :ensure t
  :commands
  (ws-butler-mode)
  :init
  (setq ws-butler-convert-leading-tabs-or-spaces t))

Appearance

Highlight delimiters

Visually separate delimiter pairs.

(use-package rainbow-delimiters
  :ensure t
  :hook
  ((clojure-mode-hook
    emacs-lisp-mode-hook
    ielm-mode-hook
    lisp-mode-hook
    racket-mode-hook) . rainbow-delimiters-mode)
  :init
  (setq rainbow-delimiters-max-face-count 3))

Highlight identifiers

Highlight source code identifiers based on their name.

(use-package color-identifiers-mode :ensure t)

(use-package rainbow-identifiers
  :ensure t
  :init
  (setq rainbow-identifiers-choose-face-function 'rainbow-identifiers-cie-l*a*b*-choose-face))

Ligatures

(use-package ligature
  :ensure t
  :commands
  (ligature-set-ligatures)
  :hook
  (prog-mode-hook . ligature-mode)
  :config
  (ligature-set-ligatures
   'prog-mode
   '("-<<" "-<" "-<-" "<--" "<---" "<<-" "<-" "->" "->>" "-->" "--->" "->-" ">-" ">>-" "<->" "<-->" "<--->" "<---->" "<!--"
     "=<<" "=<" "=<=" "<==" "<===" "<<=" "<=" "=>" "=>>" "==>" "===>" "=>=" ">=" ">>=" "<=>" "<==>" "<===>" "<====>" "<!---"
     "<------" "------>" "<=====>" "<~~" "<~" "~>" "~~>" "::" ":::" "\\/" "/\\" "==" "!=" "/=" "~=" "<>" "===" "!==" "=/=" "=!="
     ":=" ":-" ":+" "<*" "<*>" "*>" "<|" "<|>" "|>" "<." "<.>" ".>" "+:" "-:" "=:" "<******>" "(*" "*)" "++" "+++" "|-" "-|"
     "&&" "||")))

(use-package prog-mode
  :init
  (setq prettify-symbols-unprettify-at-point t))

Major modes

emacs-lisp

(add-hook 'emacs-lisp-mode-hook #'flymake-mode)
(add-hook 'emacs-lisp-mode-hook #'outline-minor-mode)

(general-define-key
 :keymaps 'emacs-lisp-mode-map
 :prefix local-leader-key
 "c" 'emacs-lisp-byte-compile
 "C" 'emacs-lisp-byte-compile-and-load
 "l" `(,(lambda () (interactive) (load-file (buffer-file-name))) :wk "Load file")
 "t" 'ert)

(custom-set-variables
 '(ad-redefinition-action 'accept)
 '(apropos-do-all t)
 '(enable-local-eval 'maybe)
 '(enable-local-variables :safe))

Specific safe local code can be specified via:

  • safe-local-variable-values
  • safe-local-eval-forms
  • safe-local-eval-function

Syntax checking

(defun elisp-flymake--load-use-package-before-compile ()
  "Ensure `use-package' macro is available for code snippets."
  (eval-when-compile
    (require 'use-package nil t)))
(advice-add 'elisp-flymake--batch-compile-for-flymake :before #'elisp-flymake--load-use-package-before-compile)

ielm

Persist ielm history.

(defvar +ielm-comint-input-ring nil)
(with-eval-after-load 'savehist
  (cl-pushnew '+ielm-comint-input-ring savehist-additional-variables :test #'equal))

(defun +ielm-set-comint-input-ring ()
  "Restore `ielm' history."
  (setq-local comint-input-ring-size 200)
  (add-hook 'kill-buffer-hook #'+ielm-save-comint-input-ring nil t)
  (when +ielm-comint-input-ring
    (setq comint-input-ring +ielm-comint-input-ring)))

(defun +ielm-save-comint-input-ring ()
  "Save `ielm' history."
  (setq +ielm-comint-input-ring comint-input-ring))

(add-hook 'ielm-mode-hook #'+ielm-set-comint-input-ring)

Packages

auto-compile

Auto-compile Elisp files.

(use-package auto-compile
  :ensure t
  :hook
  (emacs-lisp-mode-hook . auto-compile-on-load-mode)
  (emacs-lisp-mode-hook . auto-compile-on-save-mode)
  :init
  (setq auto-compile-display-buffer nil)
  (setq auto-compile-use-mode-line nil))

eros

Evaluation results in overlay.

(use-package eros
  :ensure t
  :hook
  (emacs-lisp-mode-hook . eros-mode))

highlight-quoted

(use-package highlight-quoted
  :ensure t
  :hook
  (emacs-lisp-mode-hook . highlight-quoted-mode))

package-lint-flymake

(use-package package-lint-flymake
  :ensure t
  :commands (package-lint-flymake-setup)
  :preface
  (autoload 'package-lint--provided-feature "package-lint")

  (defun package-lint-flymake-setup-when-package ()
    "Enable `package-lint-flymake' when buffer seems to be a package."
    (when (package-lint--provided-feature)
      (package-lint-flymake-setup)))
  :hook
  (emacs-lisp-mode-hook . package-lint-flymake-setup-when-package))

suggest

Discover functions.

(use-package suggest
  :ensure t
  :commands (suggest)
  :preface
  (defun +suggest-popup ()
    "Open suggest as a popup."
    (interactive)
    (let* ((window         (selected-window))
           (dedicated-flag (window-dedicated-p window)))
      (set-window-dedicated-p window t)
      (suggest)
      (set-window-dedicated-p window dedicated-flag)))
  :general
  (:keymaps
   'emacs-lisp-mode-map
   :prefix local-leader-key
   "s" '+suggest-popup))

Appearance

Regular expression escapes

Improve readability of escape characters in regular expressions.

(use-package easy-escape
  :ensure t
  :hook
  (emacs-lisp-mode-hook . easy-escape-minor-mode))

erc

(use-package erc
  :init
  (setq erc-hide-list '("JOIN" "PART" "QUIT"))
  (setq erc-prompt-for-password nil))

org

(use-package org
  :defer 5
  :preface
  (autoload 'org-get-outline-path "org-refile" nil t)
  :hook
  (org-mode-hook . auto-fill-mode)
  :general
  ("C-C a" 'org-agenda)
  (:keymaps
   'org-mode-map
   "C-'" 'nil
   "C-," 'nil)
  :init
  (setq org-agenda-files
        '("~/org/Personal.org" "~/org/Work.org"))
  (setq org-insert-heading-respect-content t)
  (setq org-log-done 'time)
  (setq org-modules nil)
  (setq org-special-ctrl-a/e t)
  
  (setq org-confirm-babel-evaluate nil)
  (setq org-edit-src-content-indentation 0)
  (setq org-src-preserve-indentation nil)
  (setq org-src-tab-acts-natively t)
  (setq org-src-window-setup 'current-window)
  
  (setq org-auto-align-tags nil)
  (setq org-tags-column 0)
  (setq org-tag-alist
        '(("@work"  . ?w)
          ("@home"  . ?h)
          ("laptop" . ?l))))

(use-package ob-plantuml
  :init
  (setq org-plantuml-exec-mode 'plantuml))

Packages

org-contrib

Unmaintained add-ons for org-mode, in this configuration org-eldoc is used.

(use-package org-contrib :ensure t)

org-babel-eval-in-repl

Evaluation of source code blocks in REPL.

(use-package org-babel-eval-in-repl
  :ensure t
  :commands
  (ober-eval-in-repl
   ober-eval-block-in-repl)
  :preface
  (autoload 'org-in-block-p "org")
  (autoload 'org-insert-heading-respect-content "org")
  (autoload 'org-meta-return "org")

  (defun +org-ctrl-return ()
    "Run `ober-eval-in-repl' if in source code block.
Fallback to `org-insert-heading-respect-content' otherwise."
    (interactive)
    (if (org-in-block-p '("src" "example"))
        (ober-eval-in-repl)
      (call-interactively #'org-insert-heading-respect-content)))
  (defun +org-meta-return ()
    "Run `ober-eval-block-in-repl' if in source code block or example block.
Fallback to `org-meta-return' otherwise."
    (interactive)
    (if (org-in-block-p '("src" "example"))
        (ober-eval-block-in-repl)
      (call-interactively #'org-meta-return)))
  :general
  (:keymaps
   'org-mode-map
   "C-<return>" '+org-ctrl-return
   "M-<return>" '+org-meta-return))

org-cliplink

Paste links from clipboard and automatically fetch title.

(use-package org-cliplink
  :ensure t
  :general
  (:keymaps
   'org-mode-map
   :prefix local-leader-key
   "l" 'org-cliplink))

org-eldoc

Eldoc support, showing path to current section.

(use-package org-eldoc
  :after eldoc
  :preface
  (defun +org-eldoc-get-breadcrumb-no-properties (string)
    "Remove properties from STRING."
    (when string (substring-no-properties string)))
  :hook
  (org-mode-hook . org-eldoc-load)
  :config
  (advice-add 'org-eldoc-get-breadcrumb :filter-return #'+org-eldoc-get-breadcrumb-no-properties))

org-noter

Annotate documents with org-mode.

(use-package org-noter :ensure t)

org-preview-html

Preview org-file in an eww buffer.

(use-package org-preview-html :ensure t)

org-radiobutton

Support for radiobuttons.

(use-package org-radiobutton
  :ensure t
  :hook
  (org-mode-hook . org-radiobutton-mode))

org-tree-slide

Presentation mode.

(use-package org-tree-slide
  :ensure t
  :hook
  (org-tree-slide-play-hook . turn-on-hide-mode-line-mode)
  (org-tree-slide-stop-hook . turn-off-hide-mode-line-mode)
  (org-tree-slide-play-hook . no-fringes)
  (org-tree-slide-stop-hook . restore-fringes)
  :general
  (:keymaps
   'org-tree-slide-mode-map
   "<right>" 'org-tree-slide-move-next-tree
   "<left>" 'org-tree-slide-move-previous-tree)
  (:keymaps
   'org-mode-map
   :prefix local-leader-key
   "p" 'org-tree-slide-mode)
  :init
  (setq org-tree-slide-header nil)
  (setq org-tree-slide-slide-in-effect nil))

ob-http

(use-package ob-http :ensure t)

Appearance

This sections makes org-mode look more beautiful and appealing.

(use-package org
  :hook
  (org-babel-after-execute-hook . org-redisplay-inline-images)
  :init
  (setq org-catch-invisible-edits 'show-and-error)
  (setq org-fontify-quote-and-verse-blocks t)
  (setq org-hide-emphasis-markers t)
  (setq org-pretty-entities t)
  (setq org-src-fontify-natively t)
  (setq org-startup-with-inline-images t))

(use-package org-modern
  :ensure t
  :hook
  (org-mode-hook . org-modern-mode)
  :init
  (setq org-modern-hide-stars t)
  (setq org-modern-table nil))

Emphasis

Allow more newlines (1 to 4) for emphasized text, useful when filling long text.

(use-package org
  :preface
  (autoload 'org-set-emph-re "org")
  :config
  (setcar (nthcdr 4 org-emphasis-regexp-components) 4)
  (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components))

Variable pitch

Use variable-pitch font but still make sure everything aligns.

(use-package org-variable-pitch
  :ensure t
  :hook
  (org-mode-hook . org-variable-pitch-minor-mode))

Headings

(use-package org
  :init
  (setq org-ellipsis "")
  (setq org-fontify-whole-heading-line t)
  (setq org-fontify-todo-headline t)
  (setq org-fontify-done-headline t))

Export

epub

(use-package ox-epub :ensure t)

HTML

(use-package ox-html
  :init
  (setq org-html-postamble nil)
  (setq org-html-validation-link nil))

LaTeX

Disable link colors.

(use-package ox-latex
  :init
  (setq org-latex-hyperref-template
        (mapconcat
         'identity
         '("\\hypersetup{"
           "pdfauthor={%a},"
           "pdftitle={%t},"
           "pdfkeywords={%k},"
           "pdfsubject={%d},"
           "pdfcreator={%c},"
           "pdflang={%L},"
           "pdfborder=0 0 0}")
         "\n")))

Add links in footnotes.

(autoload 'org-export-derived-backend-p "ox")

(defvar org-export-latex-add-link-footnotes t
  "If non-nil links will be added as footnotes if exported to latex.")

(defun org-export-latex-link-footnote (text backend _info)
  "Create a footnote for each link to retain this information for print.
If there is a URL and the export BACKEND is latex, then extract
URL into footnote from TEXT."
  (when (and org-export-latex-add-link-footnotes
             (org-export-derived-backend-p backend 'latex)
             (string-match "\\\\href{\\(.*\\)}{\\(.*\\)}" text))
    (when (cl-some (lambda (type)
                     (string-prefix-p type (match-string 1 text)))
                   '("http" "https" "ftp" "mailto" "doi"))
      (format "%s \\footnote{\\url{%s}} " text (match-string 1 text)))))

(with-eval-after-load 'ox
  (cl-pushnew #'org-export-latex-link-footnote org-export-filter-link-functions :test #'equal))

outline

Display outlines in text like files or use it in conjunction with other major modes via outline-minor-mode.

Packages

outline-minor-faces

Add faces to outline-minor-mode in order to make the headings stand out.

(use-package outline-minor-faces
  :ensure t
  :after outline
  :hook
  (outline-minor-mode-hook . outline-minor-faces-mode))

clojure

(use-package clojure-mode
  :ensure t
  :mode
  "\\.\\(clj\\|dtm\\|edn\\)\\'"
  "\\(?:build\\|profile\\)\\.boot\\'"
  ("\\.cljc\\'" . clojurec-mode)
  ("\\.cljs\\'" . clojurescript-mode))

Packages

cider

(use-package cider
  :ensure t
  :commands
  (cider-current-repl)
  :hook
  (cider-mode-hook . cider-auto-test-mode)
  :general
  (:keymaps
   'cider-mode-map
   :prefix local-leader-key
   "c" 'cider-refresh
   "t" 'cider-test-run-test
   "T" 'cider-test-run-ns-tests
   "s" 'cider-test-run-project-tests)
  :init
  (setq cider-prompt-for-symbol nil)
  (setq cider-repl-display-help-banner nil)
  (setq cider-repl-history-file (concat data-dir "cider-history")))

crystal

(use-package crystal-mode
  :ensure t
  :mode "\\(?:\\.cr\\)\\'"
  :general
  (:keymaps
   'crystal-mode-map
   :prefix local-leader-key
   "t" 'crystal-spec-line
   "T" 'crystal-spec-buffer
   "s" 'crystal-spec-all))

Packages

inf-crystal

(use-package inf-crystal
  :ensure t
  :hook
  (crystal-mode-hook . inf-crystal-minor-mode)
  :general
  (:keymaps
   'crystal-mode-map
   :prefix local-leader-key
   "r" 'inf-crystal))

csv

(use-package csv-mode
  :ensure t
  :mode
  "\\.[Cc][Ss][Vv]\\'"
  ("\\.tsv\\'" . tsv-mode)
  :init
  (setq csv-separators '("," "\t" ";")))

elasticsearch

(use-package es-mode
  :ensure t
  :mode "\\.es\\'")

elixir

(use-package elixir-mode
  :ensure t
  :mode
  "\\.elixir\\'"
  "\\.ex\\'"
  "\\.exs\\'")

Packages

alchemist

(use-package alchemist
  :ensure t
  :hook
  (elixir-mode-hook . alchemist-mode)
  :general
  (:keymaps
   'alchemist-mode-map
   :prefix nav-prefix
   "k" 'alchemist-help-search-at-point)
  (:keymaps
   'alchemist-mode-map
   :prefix local-leader-key
   "r" 'alchemist-iex-run
   "s" 'alchemist-mix-test
   "t" 'alchemist-mix-test-at-point
   "T" 'alchemist-mix-test-this-buffer))

elm

(use-package elm-mode
  :ensure t
  :mode "\\.elm\\'"
  :general
  (:keymaps
   'elm-mode-map
   :prefix local-leader-key
   "t" 'elm-test-project)
  :init
  (setq elm-format-on-save t)
  (setq elm-package-json "elm.json")
  (setq elm-tags-exclude-elm-stuff nil)
  (setq elm-tags-on-save t))

epub

Packages

nov

(use-package nov
  :ensure t
  :mode
  ("\\.epub\\'" . nov-mode)
  :preface
  (defun init-nov-delayed-render ()
    (run-with-idle-timer 0.2 nil 'nov-render-document))
  :hook
  (nov-mode-hook . init-nov-delayed-render)
  (nov-mode-hook . no-fringes)
  :init
  (setq nov-save-place-file (concat data-dir "nov-places")))

erlang

(use-package erlang
  :ensure t
  :mode
  "\\.erl$"
  "\\.app\\.src$"
  "\\.escript"
  "\\.hrl$"
  "\\.xrl$"
  "\\.yrl"
  "/ebin/.+\\.app"
  :preface
  (defun init-erlang-eunit ()
    "Setup EUnit support for `erlang-mode'."
    (require 'erlang-eunit))
  (defun init-erlang-flymake ()
    "Setup `flymake' support for `erlang-mode'."
    (require 'erlang-flymake)
    (flymake-mode 1))
  :hook
  (erlang-mode-hook . init-erlang-eunit)
  (erlang-mode-hook . init-erlang-flymake)
  :general
  (:keymaps
   'erlang-mode-map
   :prefix nav-prefix
   "k" 'erlang-man-function)
  (:keymaps
   'erlang-mode-map
   :prefix local-leader-key
   "t" 'erlang-eunit-compile-and-run-current-test
   "T" 'erlang-eunit-compile-and-run-module-tests
   "r" 'erlang-shell-display))

git

Packages

git-modes

(use-package git-modes
  :ensure t
  :mode
  ("/\\.dockerignore\\'" . gitignore-mode))

go

(use-package go-mode
  :ensure t
  :mode
  "\\.go\\'"
  ("go\\.mod\\'" . go-dot-mod-mode)
  :general
  (:keymaps
   'go-mode-map
   :prefix nav-prefix
   "k" 'godoc-at-point)
  :init
  (setq gofmt-command "goimports"))

Packages

go-eldoc

(use-package go-eldoc
  :ensure t
  :hook
  (go-mode-hook . go-eldoc-setup))

gorepl-mode

(use-package gorepl-mode
  :ensure t
  :hook
  (go-mode-hook . gorepl-mode))

groovy

(use-package groovy-mode
  :ensure t
  :mode
  "\\.g\\(?:ant\\|roovy\\|radle\\)\\'"
  "Jenkinsfile")

fish

(use-package fish-mode
  :ensure t
  :mode
  "\\.fish\\'"
  "/fish_funced\\..*\\'")

haskell

(use-package haskell-mode
  :ensure t
  :preface
  (eval-when-compile
    (require 'haskell-commands))
  :mode
  "\\.[gh]s\\'"
  "\\.hsig\\'"
  "\\.hsc\\'"
  ("\\.cabal\\'\\|/cabal\\.project\\|/\\.cabal/config\\'" . haskell-cabal-mode)
  ("\\.l[gh]s\\'" . haskell-literate-mode)
  :hook
  (haskell-mode-hook . interactive-haskell-mode)
  :general
  (:keymaps
   'interactive-haskell-mode-map
   "M-." 'nil)
  (:keymaps
   'haskell-mode-map
   :prefix local-leader-key
   "r" 'haskell-interactive-switch
   "R" 'haskell-session-change-target)
  :init
  (setq haskell-font-lock-symbols t)
  (setq haskell-process-auto-import-loaded-modules t)
  (setq haskell-process-log t)
  (setq haskell-process-show-debug-tips nil)
  (setq haskell-process-use-presentation-mode t)
  (setq haskell-stylish-on-save t)
  (setq haskell-mode-stylish-haskell-path "brittany")

  ;; Allow configuring project local cabal repl commands.
  (put 'haskell-process-args-cabal-repl
       'safe-local-variable
       (lambda (x) (cl-every #'stringp x))))

REPL

Persist REPL history.

(use-package haskell-mode
  :preface
  (defvar +haskell-interactive-global-history nil)

  (defun +haskell-interactive-save-history ()
    "Save `haskell-interactive-mode' history."
    (setq +haskell-interactive-global-history haskell-interactive-mode-history))

  (defun +haskell-interactive-load-history ()
    "Restore `haskell-interactive-mode' history."
    (add-hook 'kill-buffer-hook #'+haskell-interactive-save-history nil t)
    (when +haskell-interactive-global-history
      (setq haskell-interactive-mode-history +haskell-interactive-global-history)))
  :defines (haskell-interactive-mode-history)
  :hook
  (haskell-interactive-mode-hook . +haskell-interactive-load-history)
  :config
  (cl-pushnew '+haskell-interactive-global-history savehist-additional-variables :test #'equal))

java

Packages

gradle-mode

(use-package gradle-mode
  :ensure t
  :hook
  ((java-mode-hook kotlin-mode-hook) . gradle-mode)
  :general
  (:keymaps
   'gradle-mode-map
   :prefix local-leader-key
   "t" 'gradle-test))

java-lookup

(use-package javadoc-lookup
  :ensure t
  :general
  (:keymaps
   'java-mode-map
   :prefix nav-prefix
   "k" 'javadoc-lookup))

js

(use-package js
  :mode
  ("\\.js[mx]?\\'" . javascript-mode)
  ("\\.har\\'" . javascript-mode)
  :preface
  (defun init-js-find-file ()
    "Setup `ff-test-find-other-file' support for `js-mode'."
    (setq ff-test-search-implementation-project-directories '("src")
          ff-test-search-test-directories '("." "__tests__")
          ff-test-search-test-project-directories '("test" "spec" "__tests__")))
  :hook
  (js-mode-hook . init-js-find-file))

Packages

flymake-eslint

(use-package flymake-eslint
  :ensure t
  :init
  (put 'flymake-eslint-executable-name 'safe-local-variable #'(lambda (x) (member x '("eslint" "eslint_d"))))
  (put 'flymake-eslint-executable-args 'safe-local-variable 'stringp))

indium

Debugger

(use-package indium
  :ensure t
  :commands
  (indium-interaction-mode
   indium-client-process-live-p
   indium-switch-to-repl-buffer)
  :preface
  (defun +indium-interaction-unless-json-mode ()
    (unless (derived-mode-p 'json-mode) (indium-interaction-mode)))
  :hook
  (js-mode-hook . +indium-interaction-unless-json-mode)
  :general
  (:keymaps
   'js-mode-map
   :prefix local-leader-key
   "s" 'indium-scratch
   "o" 'indium-launch
   "O" 'indium-connect)
  (:keymaps
   'indium-debugger-mode
   :definer 'minor-mode
   "RET" 'indium-debugger-step-over))

jest

Test framework execution.

(use-package jest
  :ensure t
  :general
  (:keymaps
   'js-mode-map
   :prefix local-leader-key
   "t" 'jest-funcion-dwim
   "T" 'jest-file
   "s" 'jest-popup))

nodejs-repl

REPL for nodejs.

(use-package nodejs-repl
  :ensure t
  :commands (nodejs-repl))

json

(use-package json-mode
  :ensure t
  :mode
  "\\(?:\\(?:\\.json\\|\\.jsonld\\|\\.babelrc\\|\\.bowerrc\\|composer\\.lock\\)\\'\\)")

Syntax checker

(use-package json-mode
  :preface
  (flymake-quickdef-backend flymake-check-jsonlint
    :pre-let ((jsonlint-exec (executable-find "jsonlint")))
    :pre-check (unless jsonlint-exec (error "Cannot find jsonlint executable"))
    :write-type 'file
    :proc-form (list jsonlint-exec "-c" "-q" fmqd-temp-file)
    :search-regexp "^\\(.+\\)\: line \\([0-9]+\\), col \\([0-9]+\\), \\(.+\\)$"
    :prep-diagnostic
    (let* ((lnum (string-to-number (match-string 2)))
           (col (string-to-number (match-string 3)))
           (msg (match-string 4))
           (pos (flymake-diag-region fmqd-source lnum col))
           (beg (car pos))
           (end (cdr pos))
           (type :error))
      (list fmqd-source beg end type msg)))

  (defun init-json-mode-flymake ()
    "Setup `json-mode' integration with Flymake."
    (add-hook 'flymake-diagnostic-functions #'flymake-check-jsonlint nil t)
    (flymake-mode))
  :hook
  (json-mode-hook . init-json-mode-flymake))

Packages

json-navigator

(use-package json-navigator
  :ensure t
  :general
  (:keymaps
   'json-mode-map
   :prefix local-leader-key
   "n" 'json-navigator-navigate-region))

kotlin

(use-package kotlin-mode :ensure t)

lisp

(use-package inf-lisp
  :init
  (setq inferior-lisp-program "sbcl"))

Packages

slime

(use-package slime
  :ensure t
  :preface
  (autoload 'slime-load-file "slime")
  (autoload 'ff-test-other-file-name "ff-test")

  (defun slime-load-file-and-other-file ()
    "Load buffer file and then load other (test) file."
    (interactive)
    (dolist (file `(,buffer-file-name ,(ff-test-other-file-name)))
      (when (and file (file-exists-p file))
        (slime-load-file file))))
  :hook
  (lisp-mode-hook . slime-mode)
  :general
  (:keymaps
   'slime-mode-map
   :prefix nav-prefix
   "k" 'slime-describe-symbol)
  (:keymaps
   'slime-mode-map
   :prefix local-leader-key
   "o" 'slime
   "t" 'slime-load-file-and-other-file)
  :init
  (setq slime-contribs '(slime-fancy slime-repl)))

sly

(use-package sly :ensure t)

lua

(use-package lua-mode
  :ensure t
  :mode "\\.lua\\'"
  :commands
  (lua-get-create-process)
  :general
  (:keymaps
   'lua-mode-map
   :prefix nav-prefix
   "k" 'lua-search-documentation)
  :init
  (setq lua-documentation-function 'eww))

markdown

(use-package markdown-mode
  :ensure t
  :mode
  "\\.markdown\\'"
  "\\.md\\'"
  :hook
  (markdown-mode-hook . auto-fill-mode)
  :general
  (:keymaps
   'markdown-mode-map
   :prefix local-leader-key
   "v" 'markdown-toggle-markup-hiding)
  :init
  (setq markdown-enable-wiki-links t)
  (setq markdown-fontify-code-blocks-natively t)
  (setq markdown-header-scaling t)
  (setq markdown-hide-markup t)
  (setq markdown-italic-underscore t)
  (setq markdown-blockquote-display-char '("" ">"))
  (setq markdown-list-item-bullets '("" ""))
  (setq markdown-make-gfm-checkboxes-buttons t))

Packages

edit-indirect

Editing regions in separate buffers.

(use-package edit-indirect :ensure t)

markdown-toc

Generate Table of Contents.

(use-package markdown-toc :ensure t)

Appearance

Variable pitch

Use variable-pitch font but still make sure everything aligns.

(font-lock-add-keywords
 'markdown-mode
 '(("^[[:space:]-*+>]+" 0 'fixed-pitch append))
 'append)

Bullet lists

Pretty check-boxes

(font-lock-add-keywords
 'markdown-mode
 '(("^ *[-*+] \\[\\([Xx]\\)\\] "
    (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))

message

(use-package message
  :init
  (setq message-expand-name-standard-ui t))

nginx

(use-package nginx-mode
  :ensure t
  :mode
  "/nginx/.+\\.conf\\'"
  "nginx\\.conf\\'")

nix

(use-package nix-mode
  :ensure t
  :mode "\\.nix\\'"
  :general
  (:keymaps
   'nix-mode-map
   :prefix local-leader-key
   "f" 'nix-flake))

Packages

nix-update

(use-package nix-update
  :ensure t
  :general
  (:keymaps
   'nix-mode-map
   :prefix local-leader-key
   "u" 'nix-update-fetch))

nxml

(use-package nxml-mode
  :mode "\\.plist\\'"
  :init
  (setq nxml-slash-auto-complete-flag t))

ocaml

(use-package caml :ensure t)

(use-package tuareg
  :ensure t
  :general
  (:keymaps
   'tuareg-mode-map
   "C-x C-e" 'tuareg-eval-phrase)
  (:keymaps
   'tuareg-mode-map
   :prefix local-leader-key
   "r" 'run-ocaml))

pdf

Packages

pdf-tools

(use-package pdf-tools
  :ensure t
  :mode ("\\.pdf\\'" . pdf-view-mode))

plantuml

(use-package plantuml-mode
  :ensure t
  :mode "\\.\\(plantuml\\|pum\\|plu\\)\\'"
  :init
  (setq plantuml-default-exec-mode 'executable))

protobuf

(use-package protobuf-mode
  :ensure t
  :mode "\\.proto\\'")

python

(use-package python
  :ensure t
  :hook (python-mode-hook . indent-guide-mode)
  :general
  (:keymaps
   'python-mode-map
   :prefix local-leader-key
   "r" 'run-python)
  :config
  (put 'python-shell-interpreter 'safe-local-variable #'(lambda (x) (member x '("python" "ipython")))))

Packages

pydoc

(use-package pydoc
  :ensure t
  :general
  (:keymaps
   'python-mode-map
   :prefix nav-prefix
   "k" 'pydoc-at-point))

python-test

(use-package python-test
  :ensure t
  :general
  (:keymaps
   'python-mode-map
   :prefix local-leader-key
   "t" 'python-test-project)
  :init
  (setq python-test-backend 'pytest)
  :config
  (setq python-test-project-root-files
        (append '("README.md") python-test-project-root-files)))

racket

(use-package racket-mode
  :ensure t
  :mode "\\.rkt[dl]?\\'"
  :general
  (:keymaps
   'racket-mode-map
   :prefix nav-prefix
   "k" 'racket-doc)
  (:keymaps
   'racket-mode-map
   :prefix local-leader-key
   "t" 'racket-test))

Packages

flymake-racket

(use-package flymake-racket
  :ensure t
  :hook
  (racket-mode-hook . flymake-racket-add-hook))

rego

(use-package rego-mode :ensure t)

rst

(use-package rst
  :preface
  (defvar rst-adornment-regexp nil
    "Regular expression to match adornments.")
  :hook
  (rst-mode-hook . auto-fill-mode)
  :config
  (setq rst-adornment-regexp
        (concat "^[" rst-adornment-chars "]\\{3,\\}$")))

Appearance

Variable pitch

Use variable-pitch font but still make sure everything aligns.

(font-lock-add-keywords
 'rst-mode
 '(("^[[:space:]-*+]+\\(\\[.\\]\\)?" 0 'fixed-pitch append))
 'append)

Heading

Hide heading adornments.

(defun +rst-hide-heading-adornment ()
  "Hide heading adornment for `rst-mode'."
  (interactive)
  (hide-lines-matching rst-adornment-regexp))
(add-hook 'rst-mode-hook #'+rst-hide-heading-adornment)

Bullet lists

Pretty check-boxes as well as bullet lists.

(font-lock-add-keywords
 'rst-mode
 '(("^ *\\([-*+]\\) "
    (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))
   ("^ *[-*+] \\[\\([Xx]\\)\\] "
    (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ""))))))

ruby

(use-package ruby-mode
  :preface
  (defun init-ruby-find-file ()
    "Setup `ff-test-find-other-file' support for `ruby-mode'."
    (setq ff-test-suffixes '("_test" "_spec")
          ff-test-search-implementation-project-directories '("app" "lib")
          ff-test-search-test-project-directories '("test" "spec")))
  :hook
  (ruby-mode-hook . init-ruby-find-file)
  :init
  (setq ruby-align-chained-calls t)
  :config
  (with-eval-after-load 'hideshow
    (cl-pushnew `(ruby-mode
                  ,(rx (or "def" "class" "module" "do" "{" "[")) ; Block start
                  ,(rx (or "}" "]" "end"))                       ; Block end
                  ,(rx bol
                       (or (+ (zero-or-more blank) "#") "=begin")) ; Comment start
                  ruby-forward-sexp nil)
                hs-special-modes-alist
                :test #'equal)))

Testing

(general-define-key
 :keymaps 'minitest-mode-map
 :prefix local-leader-key
 "t" 'minitest-verify-single
 "T" 'minitest-verify
 "s" 'minitest-verify-all)

(general-define-key
 :keymaps 'rspec-mode-map
 :prefix local-leader-key
 "t" 'rspec-verify-single
 "T" 'rspec-verify
 "s" 'rspec-verify-all)

Packages

inf-ruby

(use-package inf-ruby
  :ensure t
  :hook
  (ruby-mode-hook . inf-ruby-minor-mode)
  ;; Auto breakpoint
  (compilation-filter . inf-ruby-auto-enter)
  :general
  (:keymaps
   'ruby-mode-map
   :prefix local-leader-key
   "r" 'inf-ruby)
  :init
  (setq inf-ruby-default-implementation "pry"))

minitest

(use-package minitest
  :ensure t
  :hook
  (ruby-mode-hook . minitest-enable-appropriate-mode))

rake

(use-package rake
  :ensure t
  :init
  (setq rake-completion-system 'default)
  (setq rake-cache-file (expand-file-name "rake.cache" cache-dir)))

rspec-mode

(use-package rspec-mode
  :ensure t
  :hook
  (ruby-mode-hook . rspec-enable-appropriate-mode)
  :init
  (setq rspec-use-relative-path t)
  (setq rspec-use-opts-file-when-available nil)
  (setq rspec-command-options "--format progress"))

ruby-refactor

(use-package ruby-refactor
  :ensure t
  :hook
  (ruby-mode-hook . ruby-refactor-mode))

yard-mode

(use-package yard-mode
  :ensure t
  :hook
  (ruby-mode-hook . yard-mode))

yari

(use-package yari
  :ensure t
  :general
  (:keymaps
   'ruby-mode-map
   :prefix nav-prefix
   "k" 'yari)
  (:keymaps
   'help-command-map
   "R" 'yari))

rust

(use-package rustic
  :ensure t
  :mode
  ("\\.rs\\'" . rustic-mode)
  :general
  (:keymaps
   'rustic-mode-map
   :prefix local-leader-key
   "t" 'rustic-cargo-test)
  :init
  (setq rustic-lsp-client 'eglot)
  (with-eval-after-load 'org
    (cl-pushnew '("rust" . rustic) org-src-lang-modes :test #'equal))
  :config
  (require 'eglot))

scala

(use-package scala-mode
  :ensure t
  :mode "\\.\\(scala\\|sbt\\)\\'"
  :preface
  (defun init-scala-prettify-symbols ()
    (setq prettify-symbols-alist scala-prettify-symbols-alist)
    (prettify-symbols-mode 1))
  :hook
  (scala-mode-hook . init-scala-prettify-symbols)
  :init
  (setq scala-indent:align-parameters t))

Packages

sbt-mode

(use-package sbt-mode :ensure t)

sh

(use-package sh-script
  :general
  (:keymaps
   'sh-mode-map
   :prefix nav-prefix
   "k" 'man)
  :init
  ;; Use regular indentation for line-continuation
  (setq sh-indent-after-continuation 'always))

Packages

flymake-shellcheck

(use-package flymake-shellcheck
  :ensure t
  :hook
  (sh-mode-hook . flymake-mode)
  (sh-mode-hook . flymake-shellcheck-load))

sql

(use-package sql
  :init
  (setq sql-mysql-options '("--protocol=tcp" "--prompt=" "--disable-pager")))

terraform

(use-package terraform-mode
  :ensure t
  :mode "\\.tf$")

typescript

(use-package typescript-mode
  :ensure t
  :mode "\\.ts$"
  :preface
  (defun init-typescript-find-file ()
    "Setup `ff-test-find-other-file' support for `typescript-mode'."
    (setq ff-test-search-implementation-project-directories '("src")
          ff-test-search-test-directories '("." "__tests__")
          ff-test-search-test-project-directories '("test" "spec" "__tests__")))
  :hook
  (typescript-mode-hook . init-typescript-find-file)
  :general
  (:keymaps
   'typescript-mode-map
   "C-c '" 'nil))

Packages

ts-comint

TypeScript REPL.

(use-package ts-comint
  :ensure t
  :init
  (setq ts-comint-program-command "ts-node"))

jest

Test framework execution.

(use-package jest
  :ensure t
  :general
  (:keymaps
   'typescript-mode-map
   :prefix local-leader-key
   "t" 'jest-function-dwim
   "T" 'jest-file
   "s" 'jest-popup))

web

Packages

cakecrumbs

Display current path for HTML/XML/CSS.

(use-package cakecrumbs
  :ensure t
  :defer 3
  :commands
  (cakecrumbs-auto-setup)
  :config
  (cakecrumbs-auto-setup))

web-mode

(use-package web-mode
  :ensure t
  :mode "\\.\\(phtml\\|php\\|[agj]sp\\|as[cp]x\\|erb\\|djhtml\\|html?\\|hbs\\|ejs\\|jade\\|swig\\|tmpl\\)\\'"
  :init
  (setq web-mode-enable-html-entities-fontification t)
  ;; Highlight enclosing tags of the element under cursor
  (setq web-mode-enable-current-element-highlight t)
  ;; No extra indentation for blocks.
  (setq web-mode-script-padding 0)
  (setq web-mode-style-padding 0))

yaml

(use-package yaml-mode
  :ensure t
  :mode "\\.\\(e?ya?\\|ra\\)ml\\'"
  :hook (yaml-mode-hook . indent-guide-mode))

Tools

E-mail

Packages

bbdb

Store contacts.

(use-package bbdb :ensure t)

gmail2bbdb

Import contacts from Gmail vCard data.

(use-package gmail2bbdb
  :ensure t
  :init
  (setq gmail2bbdb-bbdb-file bbdb-file)
  (setq gmail2bbdb-exclude-people-without-name t))

Focus

Dim out the surrounding text except the current focused paragraph or expression.

(use-package focus :ensure t)

Indentation guides

(use-package indent-guide
  :ensure t
  :init
  (setq indent-guide-char "\x2502"))

Pomodoro

(use-package redtick
  :ensure t
  :general
  (:prefix
   launch-prefix
   "r" 'redtick))

Session

Support for restoring previous session on Emacs restart. This adds a few tweaks that makes it really useful.

  • Support for persistent undo history via buffer-undo-list.
  • Ignore saving of gz files as it seems not to properly load those.
  • Restore no buffers to begin with to avoid slow starts with huge sessions.
(use-package desktop
  :demand t
  :init
  (setq desktop-files-not-to-save "\\(\\`/[^/:]*:\\|(ftp)\\|\\.gz\\'\\)")
  (setq desktop-restore-eager 0)
  (setq desktop-save (daemonp))
  (setq desktop-load-locked-desktop (daemonp))
  :config
  (dolist (var '(buffer-undo-list))
    (cl-pushnew var desktop-locals-to-save :test #'equal))
  (desktop-save-mode 1))

Speed reading

(use-package spray
  :ensure t
  :general
  (:prefix
   launch-prefix
   "s" 'spray-mode)
  :init
  (setq spray-height 500)
  (setq spray-margin-left 2)
  :config
  (setq spray-unsupported-minor-modes
        (append '(beacon-mode centered-window-mode visual-fill-column-mode)
                spray-unsupported-minor-modes)))

Spell checking

(use-package flyspell
  :hook
  ((message-mode-hook
    org-mode-hook
    text-mode-hook) . flyspell-mode)
  (prog-mode-hook . flyspell-prog-mode)
  :general
  (:keymaps
   'flyspell-mode-map
   "C-," 'nil
   "C-." 'nil)
  :init
  (setq flyspell-persistent-highlight nil))
(use-package flyspell-correct
  :ensure t
  :general
  (:keymaps
   'flyspell-mode-map
   "C-;" 'flyspell-correct-wrapper))

Automatic language detection that updates the spell checker.

(use-package guess-language
  :ensure t
  :commands
  (guess-language-mode))

Terminal

Color

Avoid color when possible.

(setenv "NO_COLOR" "1")

eshell

(use-package eshell
  :preface
  (eval-when-compile
    (require 'em-hist)
    (require 'em-term))
  (defvar eshell-visual-commands)
  (defun init-eshell-define-keys ()
    (general-define-key
     :keymaps 'eshell-mode-map
     "RET" '+eshell-expand-abbrev-and-send-input
     "<tab>" 'completion-at-point))

  (defun init-eshell-set-visual-commands ()
    (setq eshell-visual-commands
          (append
           '("fish" "ghcid" "jshell" "most" "ssh" "tail" "tsun" "watch")
           eshell-visual-commands)))
  :hook
  (eshell-mode-hook . abbrev-mode)
  (eshell-mode-hook . init-eshell-define-keys)
  (eshell-mode-hook . init-eshell-set-visual-commands)
  :general
  ("C-!" 'eshell)
  (:prefix
   launch-prefix
   "t" 'eshell)
  :init
  (setq eshell-buffer-maximum-lines 20000)
  (setq eshell-history-size 1000)
  (setq eshell-hist-ignoredups t)
  (setq eshell-error-if-no-glob t)
  (setq eshell-destroy-buffer-when-process-dies t)
  (autoload 'eshell-smart-initialize "em-smart"))

Abbreviations

Fish-like abbreviations that expand on space or enter.

(use-package eshell
  :preface
  (autoload 'eshell-parse-arguments "esh-arg")
  (autoload 'eshell-bol "esh-mode")
  (autoload 'eshell-send-input "esh-mode")

  (defun +eshell-expand-abbrev-and-send-input ()
    "Expand abbreviation and send input to `eshell'."
    (interactive)
    (expand-abbrev)
    (call-interactively #'eshell-send-input))

  (defun +eshell-abbrev-expand-p ()
    "Return t if abbreviation should be expanded.
Expansion should happen when abbreviation is at the beginning of
the line or after an eshell operator."
    (let* ((end (point-marker))
           (begin (save-excursion (eshell-bol) (point)))
           (args (catch 'eshell-incomplete (eshell-parse-arguments begin end))))
      (or (= 1 (length args))
          (let ((last-two-args (last args 2)))
            (and (consp (car last-two-args))
                 (eq (caar last-two-args)
                     'eshell-operator))))))
  :init
  (define-abbrev-table 'eshell-mode-abbrev-table
    '(("base64" "base64 -w0")
      ("d" "docker")
      ("dim" "docker images")
      ("dp" "docker ps")
      ("dc" "docker-compose")
      ("dcl" "docker-compose logs")
      ("e" "find-file-other-window")
      ("E" "dired")
      ("gd" "magit-diff-unstaged")
      ("gds" "magit-diff-staged")
      ("gs" "magit-status")
      ("time" "time -p")
      ("tree" "tree -a")
      ("week" "date '+%V'"))
    :enable-function #'+eshell-abbrev-expand-p))

Packages

eshell-fringe-status

Command execution status indicator in the fringe.

(use-package eshell-fringe-status
  :ensure t
  :hook
  (eshell-mode-hook . eshell-fringe-status-mode))
esh-autosuggest

Autosuggestions from history similar to fish.

(use-package esh-autosuggest
  :ensure t
  :hook
  (eshell-mode-hook . esh-autosuggest-mode)
  :general
  (:keymaps
   'esh-autosuggest-active-map
   "C-e" 'company-complete-selection))
fish-completion

Populate auto-completions from fish.

(use-package fish-completion
  :ensure t
  :hook
  (eshell-mode-hook . fish-completion-mode))

vterm

Fast terminal emulator utilizing libvterm.

(use-package vterm
  :ensure t
  :general
  (:prefix
   launch-prefix
   "T" 'vterm)
  :init
  (setq vterm-max-scrollback 100000)
  (setq vterm-shell "fish"))

Packages

vterm-toggle
(use-package vterm-toggle
  :ensure t
  :general
  ("C-M-!" 'vterm-toggle)
  (:keymaps
   'vterm-mode-map
   "C-<return>" 'vterm-toggle-insert-cd))

Undo

vundo

Visual undo history navigation.

(use-package vundo
  :ensure t
  :general
  (:prefix
   launch-prefix
   "u" 'vundo))

Testing

Packages that I am currently testing or evaluating.

adaptive-wrap

(use-package adaptive-wrap :ensure t)

affe

(use-package affe :ensure t)

annotate

(use-package annotate :ensure t)

awscli-capf

(use-package awscli-capf :ensure t)

cape

(use-package cape :ensure t)

comment-or-uncomment-sexp

(use-package comment-or-uncomment-sexp :ensure t)

dtrt-indent

(use-package dtrt-indent
  :ensure t
  :hook
  (after-init-hook . dtrt-indent-global-mode)
  :init
  (setq dtrt-indent-ignore-single-chars-flag t)
  (setq dtrt-indent-verbosity 0))

eglot-x

(use-package eglot-x :ensure t)

elfeed

(use-package elfeed :ensure t)

elsa

(use-package elsa :disabled t)

epithet

(use-package epithet :ensure t)

fancy-dabbrev

(use-package fancy-dabbrev :ensure t)

fit-text-scale

(use-package fit-text-scale :ensure t)

freeze-it

(use-package freeze-it :ensure t)

gif-screencast

(use-package gif-screencast :ensure t)

iscroll

(use-package iscroll :ensure t
  :hook
  (org-mode-hook . iscroll-mode))

messages-are-flowing

(use-package messages-are-flowing :ensure t)

multicolumn

(use-package multicolumn :ensure t)

native-complete

(use-package native-complete :ensure t)

org-fragtog

(use-package org-fragtog :ensure t)

org-roam

(use-package org-roam :ensure t)

popper

(use-package popper
  :ensure t
  :hook
  (after-init-hook . popper-mode)
  :init
  (setq popper-reference-buffers
        '("\\*Messages\\*"
          "Output\\*$"
          help-mode
          compilation-mode)))

shrface

(use-package shrface :ensure t)

stem-reading-mode

(use-package stem-reading-mode :ensure t)

terraform-doc

(use-package terraform-doc :ensure t)

theme-magic

(use-package theme-magic :ensure t)

virtual-auto-fill

(use-package virtual-auto-fill :ensure t)

Private

(require 'private nil t)

Tasks

Look into skewer packages

Look into modal editing with objed

Look into generic code execution via isend-mode

Look into set-selective-display

Look into semantic-decoration-mode

Add binding for org-noter

Group editing bindings under some logical prefix

Improve window keys

Improve find char keys

Extract history persistance code (persistory)

  • comint
  • ielm (via comint and savehist)
  • haskell-interactive (via savehist)

Enable auto-revert/save again?

Wrap lines by default?

  • When writing code on a single line, to later let it be expanded by the formatter.

Only scale images that are larger than size X in org mode

Use ielm as REPL for org-mode

Use file-name as base for work-log date

Daily tasks

Write ghq tool using completing-read

Use windmove? Perhaps with windmove-create-window t

Use vc-mode commands more C-x v g, C-x v v and C-x v =.

  • Also vc-dir and the vc-git-stash (can stash individual files)
  • Diff merge-base C-x v M D, C-x v M L
  • vc-log-search

Use ChangeLog entries C-x 4 a

Use browse-url-add-buttons to make links clickable in more places?

Create xref-quick-peek (inspired by xref-posframe or source-peek)

Create macros via checking C-h l (‘view-lossage’) then it can be used together with edit-last-kbd-macro.

Is it useful to use highlight-regexp?

solarized colorless config?

Split theme into several sections

  • One theme to set big headers and such things
  • One theme to highlight errors
  • One theme for the mode-line

Flymake + Elsa

Add minions menu to mode-line-major-mode-keymap (S-mouse-3)

Evaluate skeleton

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].