/emacs

Emacs ORG Config

Emacs Configuration

A lightweight, literate Emacs 29+ ONLY configuration.

To install, open this file in Emacs 29+ & evaluate the code block below, by placing the cursor in between the #+begin_src and #+end_src lines and then pressing C-c C-c.

(org-babel-tangle) (load init-file)

This will “tangle” all the code blocks below into ~/.emacs.d/init.el and then evaluate it.

Be sure to back up your current ~/.emacs.d with something like mv ~/.emacs.d{,.bak}.

Use Package

This is part of Emacs from version 29 and this configuration depends on it, so turn it on first.

(setq use-package-enable-imenu-support t
      use-package-always-defer t
      use-package-verbose t)
(require 'use-package)

Settings

Auto-revert

(add-hook 'after-init-hook 'global-auto-revert-mode) ;; reload if file changed on disk

Auto-save

(setq auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t)))
(setq auto-save-timeout 5)

Backups

(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
(setq backup-by-copying t) ;; copy files, don't rename them.
(setq delete-old-versions t)
(setq kept-new-versions 12)
(setq kept-old-versions 12)

Bell

(setq ring-bell-function 'ignore)
(setq visible-bell 1)

Bookmarks

(setq bookmark-save-flag 1) ;; always save bookmarks to file

Clipboard

(setq select-enable-clipboard t)
;; (setq select-enable-primary t) ;; breaks standard yank meaning that following
;; doesn't work as expected:
(delete-selection-mode t)
(setq x-select-enable-clipboard-manager nil)
(setq save-interprogram-paste-before-kill t)

Code Style

(setq c-default-style "bsd")
(setq c-basic-offset 4)
(setq css-indent-offset 2)
(setq js-indent-level 2)
;; If indent-tabs-mode is t, it may use tab, resulting in mixed spaces and tabs
(setq-default indent-tabs-mode nil)
;; make tab key do indent first then completion.
(setq-default tab-always-indent 'complete)

Column Number

(add-hook 'after-init-hook 'column-number-mode)

Compilation Scroll Output

Place point after complilation error

(setq compilation-scroll-output 'first-error)

Completions

;; I'm using icomplete for my completions, and so set this to nil to avoid
;; duplicate completions. Set this to true if not using an alternative for
;; completions. ie) icomplete, ido, vertico, ivy, helm, etc
(setq completion-auto-help nil)
(setq completion-show-help nil) ; refers to the msg at the top of the buffer
(setq completion-auto-wrap t)
(setq completion-auto-select nil) ; means focus doesn't go to the buffer
(setq completions-format 'one-column)
(setq completions-header-format nil) ; remove the rest of the header msg
(setq completions-max-height 20)
(setq completion-styles '(initials flex partial-completion substring basic))
(keymap-set completion-in-region-mode-map "C-p" #'minibuffer-previous-completion)
(keymap-set completion-in-region-mode-map "C-n" #'minibuffer-next-completion)
(setq read-answer-short t)
(setq read-buffer-completion-ignore-case t)
(setq read-file-name-completion-ignore-case t)

Customize settings file

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

Delete all on backspace

(setq backward-delete-char-untabify-method 'all)

Emacsclient executable

;; https://emacs.stackexchange.com/a/31061
(when (equal system-type 'windows-nt)
  (if (file-readable-p "C:/Program Files/Emacs/x86_64/bin/emacsclient.exe")
      (setq-default with-editor-emacsclient-executable "C:/Program Files/Emacs/x86_64/bin/emacsclient.exe")
    (setq-default with-editor-emacsclient-executable nil)))

Enable all advanced features

(setq disabled-command-function nil) ;; enable all "advanced" features

Encoding

(setq-default buffer-file-coding-system 'utf-8-unix)
(setq-default default-buffer-file-coding-system 'utf-8-unix)
(setq coding-system-for-read 'utf-8-unix)
(setq coding-system-for-write 'utf-8-unix)
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8-unix)
(prefer-coding-system 'utf-8-unix)

Encryption

(setq epa-file-cache-passphrase-for-symmetric-encryption t)
(setf epg-pinentry-mode 'loopback)

Final newline

(setq require-final-newline t) ;; useful for crontab

History

(setq history-length t) ; no truncation
(setq history-delete-duplicates t)

Initial startup

(setq inhibit-startup-screen t)
(setq initial-scratch-message nil)
(setq initial-major-mode 'fundamental-mode)

Kill Ring Duplicates

(setq kill-do-not-save-duplicates t)

Minibuffer

(setq minibuffer-eldef-shorten-default t) ; help at point
(keymap-set minibuffer-local-map "C-p" #'minibuffer-previous-completion)
(keymap-set minibuffer-local-map "C-n" #'minibuffer-next-completion)

More extensive apropos

(setq apropos-do-all t)

More Messages

(setq message-log-max 10000)

Mouse yank at point

(setq mouse-yank-at-point t)

Native Compilation

Compile elc files as needed, in a deferred/async manner.

(when (fboundp 'native-compile-async)
  (setq comp-deferred-compilation t
        comp-deferred-compilation-black-list '("/mu4e.*\\.el$")))

Dealing with system-wide packages:

Some packages like for example mu4e are located in /usr and need root permissions to be compiled, for them you can run

sudo emacs -Q -batch -L . -f batch-native-compile *.el

No lockfiles

(setq create-lockfiles nil) ;; prevent creation of .#myfile.ext

Passwords

(setq password-cache t) ; enable password caching
(setq password-cache-expiry 3600) ; for one hour (time in secs)

Pop mark on repeat

(setq set-mark-command-repeat-pop t) ;; repeating C-SPC after popping, pops it

Prefer newer over compiled

(setq load-prefer-newer t) ;; if init.elc is older, use newer init.el

Preferred programs

;; (when (executable-find "rg") (setq grep-program "rg"))
;; (when (executable-find "fd") (setq find-program "fd"))
(when (executable-find "aspell") (setq ispell-program-name "aspell"))

Windows Find

First install GnuWin32 binaries from here.

Or if you want all the GNU tools on your Windows box - use this.

(setq find-program "C:/Program Files (x86)/GnuWin32/bin/find.exe")
(setq grep-program "C:/Program Files (x86)/GnuWin32/bin/grep.exe")
(setq xargs-program "C:/Program Files (x86)/GnuWin32/bin/xargs.exe")

Repeat on final keystroke

(setq repeat-on-final-keystroke t)

Safe Local Variables

This variables are added to the end of this file.

(setq safe-local-variable-values
      '((eval setq org-confirm-babel-evaluate 'nil)
        (eval setq init-file (expand-file-name "init.el" user-emacs-directory))
        (eval add-hook 'after-save-hook 'org-html-export-to-html nil t)
        (eval add-hook 'after-save-hook 'org-latex-export-to-pdf nil t)
        (eval add-hook 'after-save-hook 'org-twbs-export-to-pdf nil t)
        (eval add-hook 'after-save-hook 'org-babel-tangle nil t)
        (eval add-hook 'after-save-hook (lambda nil (org-export-to-file 'html "index.html") (org-latex-export-to-pdf)) nil t)
        (org-html-validation-link)))

Scrolling

(setq scroll-step 4)
(setq scroll-margin 2)
(setq scroll-conservatively 4)
(setq scroll-preserve-screen-position t)
(pixel-scroll-precision-mode)

Sentence End Spaces

(setq sentence-end-double-space nil)

Sort Fold Case

(setq sort-fold-case t)

Truncate Lines

(set-default 'truncate-lines t)

Undo limits

(setq undo-limit 80000000)
(setq undo-strong-limit 90000000)

Undo save

(add-to-list 'desktop-locals-to-save 'buffer-undo-list)

Uniquify

(setq uniquify-buffer-name-style 'forward)
(setq uniquify-strip-common-suffix t)
(setq uniquify-after-kill-buffer-p t)

User info

(setq user-full-name "Toby Slight")
(setq user-mail-address "tslight@pm.me")

View mode

(setq view-read-only t) ; scroll with SPC & S-SPC

Windows

https://stackoverflow.com/a/7998271 https://emacs.stackexchange.com/a/52721 https://emacs.stackexchange.com/q/39034

(setq split-width-threshold nil)
;; (setq split-window-preferred-function 'split-window-really-sensibly)
(setq auto-window-vscroll nil)

Logging stuff

https://www.reddit.com/r/emacs/comments/l42oep/suppress_nativecomp_warnings_buffer/

(setq warning-minimum-level :error)
(setq warning-suppress-types '((comp) (comp) (comp)))
(setq comp-async-report-warnings-errors 'nil)
(setq native-comp-async-report-warnings-errors 'nil)

Yes or no

(fset 'yes-or-no-p 'y-or-n-p) ;; never have to type full word
(setq confirm-kill-emacs 'y-or-n-p)

Keybindings

Buffers

(keymap-global-set "C-x C-b" 'ibuffer)
(keymap-global-set "C-x k" 'kill-this-buffer)
(keymap-global-set "C-c f r" 'rename-visited-file)

Editing

(keymap-global-set "C-z" 'zap-up-to-char) ; suspend is still bound to C-x C-z
(keymap-global-set "M-z" 'zap-to-char)

Remap some default to saner options

(keymap-global-set "M-;" 'comment-line)         ; comment-dwim by default
(keymap-global-set "C-M-;" 'comment-dwim)       ; unbound by default
(keymap-global-set "C-x C-;" 'comment-box)      ; comment-line by default

Always do what I mean!

(global-set-key [remap capitalize-word] 'capitalize-dwim)
(global-set-key [remap downcase-word] 'downcase-dwim)
(global-set-key [remap upcase-word] 'upcase-dwim)

Frames

(keymap-global-set "C-<f10>" 'toggle-frame-maximized)
(keymap-global-set "C-<f11>" 'toggle-frame-fullscreen)
(keymap-global-set "C-s-f" 'toggle-frame-fullscreen)
(keymap-global-set "C-s-m" 'toggle-frame-maximized)

Menubar

(keymap-global-set "S-<f10>" 'menu-bar-mode)

Special mode

;; for help modes, and simple/special modes
(keymap-set special-mode-map "n" #'forward-button)
(keymap-set special-mode-map "p" #'backward-button)
(keymap-set special-mode-map "f" #'forward-button)
(keymap-set special-mode-map "b" #'backward-button)
(keymap-set special-mode-map "n" #'widget-forward)
(keymap-set special-mode-map "p" #'widget-backward)
(keymap-set special-mode-map "f" #'widget-forward)
(keymap-set special-mode-map "b" #'widget-backward)

Theme/UI

Maximize on startup

(setq default-frame-alist '((undecorated . t)
                            (fullscreen . maximized)
                            (vertical-scroll-bars . nil)))
;; (setq frame-resize-pixelwise t) ;; jwm resize fix

Setup Frame for Emacsclient

(defun ts-after-make-frame (frame)
  "Add custom settings after making the FRAME."
  (select-frame frame)
  (load-theme 'modus-vivendi)
  (if (display-graphic-p)
      (cond ((eq system-type 'windows-nt) (set-frame-font "Cascadia Mono 11" nil t))
            ((eq system-type 'darwin) (set-frame-font "Monaco 12" nil t))
            ((eq system-type 'gnu/linux) (set-frame-font "Monospace 11" nil t))
            (t (set-frame-font "Monospace 11" nil t)))))

(if (daemonp)
    (add-hook 'after-make-frame-functions #'ts-after-make-frame (selected-frame))
  (ts-after-make-frame (selected-frame)))

Turn off UI elements

(if (not (eq system-type 'darwin))
    (if (fboundp 'menu-bar-mode) (menu-bar-mode -1)))
(if (fboundp 'scroll-bar-mode) (scroll-bar-mode -1))
(if (fboundp 'tool-bar-mode) (tool-bar-mode -1))
(if (fboundp 'tooltip-mode) (tooltip-mode -1))
(if (fboundp 'set-horizontal-scroll-bar-mode)
    (set-horizontal-scroll-bar-mode nil))

Buffer Functions

Encoding

(defun ts-convert-to-unix-coding-system ()
  "Change the current buffer's file encoding to unix."
  (interactive)
  (let ((coding-str (symbol-name buffer-file-coding-system)))
    (when (string-match "-\\(?:dos\\|mac\\)$" coding-str)
      (set-buffer-file-coding-system 'unix))))

(keymap-global-set "C-x RET u" 'ts-convert-to-unix-coding-system)

(defun ts-hide-dos-eol ()
  "Do not show ^M in files containing mixed UNIX and DOS line endings."
  (interactive)
  (setq buffer-display-table (make-display-table))
  (aset buffer-display-table ?\^M []))

(add-hook 'find-file-hook 'ts-hide-dos-eol)

Indent Buffer

(defun ts-indent-buffer ()
  "Indent the contents of a buffer."
  (interactive)
  (indent-region (point-min) (point-max)))

(keymap-global-set "M-i" 'ts-indent-buffer)

Last buffer

(defun ts-last-buffer ()
  "Switch back and forth between two buffers easily."
  (interactive)
  (switch-to-buffer (other-buffer (current-buffer) 1)))

(keymap-global-set "C-c b" 'ts-last-buffer)

Nuke buffers

(defun ts-nuke-buffers ()
  "Kill all buffers, leaving *scratch* only."
  (interactive)
  (mapc
   (lambda (buffer)
     (kill-buffer buffer))
   (buffer-list))
  (if current-prefix-arg
      (delete-other-windows)))

(keymap-global-set "C-c M-n" 'ts-nuke-buffers)

Remove from buffer

(defun ts-remove-from-buffer (string)
  "Remove all occurences of STRING from the whole buffer."
  (interactive "sString to remove: ")
  (save-match-data
    (save-excursion
      (let ((count 0))
        (goto-char (point-min))
        (while (re-search-forward string (point-max) t)
          (setq count (+ count 1))
          (replace-match "" nil nil))
        (message (format "%d %s removed from buffer." count string))))))

(keymap-global-set "C-c k" 'ts-remove-from-buffer)

Save buffers silently

(defun ts-save-buffers-silently ()
  "Save all open buffers without prompting."
  (interactive)
  (save-some-buffers t)
  (message "Saved all buffers :-)"))

(keymap-global-set "C-c s" 'ts-save-buffers-silently)

Editing Functions

Aligning symbols

Some handy functions to make aligning symbols less painful.

(defun ts-align-symbol (begin end symbol)
  "Align any SYMBOL in region (between BEGIN and END)."
  (interactive "r\nsEnter align symbol: ")
  (align-regexp begin end (concat "\\(\\s-*\\)" symbol) 1 1))

(keymap-global-set "C-c a" 'ts-align-symbol)

(defun ts-align-numbers (begin end)
  "Align numbers in region (between BEGIN and END)."
  (interactive "r")
  (ts-align-symbol begin end "[0-9]+"))

(keymap-global-set "C-c #" 'ts-align-numbers)

(defadvice align-regexp (around align-regexp-with-spaces activate)
  "Force alignment commands to use spaces, not tabs."
  (let ((indent-tabs-mode nil)) ad-do-it))

Auto-fill

This function only applies to comment blocks, comments, example blocks and paragraphs. Also, as a special case, re-align table when point is at one.

(defun ts-fill-or-unfill ()
  "Like `fill-paragraph', but unfill if used twice."
  (interactive)
  (let ((fill-column
         (if (eq last-command 'ts-fill-or-unfill)
             (progn (setq this-command nil)
                    (point-max))
           fill-column)))
    (call-interactively #'fill-paragraph)))

(defun ts-unfill-region (beg end)
  "Unfill the region from BEG to END.
Joining text paragraphs into a single logical line. This is
useful, e.g., for use with function `visual-line-mode'."
  (interactive "*r")
  (let ((fill-column (point-max)))
    (fill-region beg end)))

(global-set-key [remap fill-paragraph] 'ts-fill-or-unfill)
(keymap-global-set "C-M-q" 'ts-unfill-region)

(setq-default fill-column 79)

(add-hook 'org-mode-hook 'auto-fill-mode)
(add-hook 'text-mode-hook 'auto-fill-mode)

Beginning of line

(defun ts-move-beginning-of-line ()
  "Move point back to indentation.

If there is any non blank characters to the left of the cursor.
Otherwise point moves to beginning of line."
  (interactive)
  (if (= (point) (save-excursion (back-to-indentation) (point)))
      (beginning-of-line)
    (back-to-indentation)))

(global-set-key [remap move-beginning-of-line] 'ts-move-beginning-of-line)

Delete inside delimiters

(defun ts-delete-inside-delimiters (arg)
  "Deletes the text within parentheses, brackets or quotes.
With prefix ARG, delete delimiters too."
  (interactive "P")
  ;; Search for a match on the same line, don't delete across lines
  (search-backward-regexp "[[{(<\"\']" (line-beginning-position))
  (forward-char)
  (let ((lstart (point)))
    (search-forward-regexp "[]})>\"\']" (line-end-position))
    (backward-char)
    (if arg
        (kill-region (- lstart 1) (+ (point) 1))
      (kill-region lstart (point)))))

(keymap-global-set "C-c d" 'ts-delete-inside-delimiters)

Generate a numbered list

(defun ts-generate-numbered-list (start end char)
  "Create a numbered list from START to END.  Using CHAR as punctuation."
  (interactive "nStart number:\nnEnd number:\nsCharacter:")
  (let ((x start))
    (while (<= x end)
      (insert (concat (number-to-string x) char))
      (newline)
      (setq x (+ x 1)))))

Kill Region

(defun ts-kill-region (arg)
  "Cut region or current ARG lines to kill ring."
  (interactive "p")
  (let (p1 p2)
    (if (use-region-p)
        (progn (setq p1 (region-beginning))
               (setq p2 (region-end)))
      (progn (setq p1 (line-beginning-position))
             (setq p2 (line-beginning-position (+ arg 1)))))
    (kill-region p1 p2)))

(global-set-key [remap kill-region] 'ts-kill-region)

Kill Ring Save

(defun ts-kill-ring-save (arg)
  "Copy region or current ARG lines to kill ring."
  (interactive "p")
  (let (p1 p2)
    (if (use-region-p)
        (progn (setq p1 (region-beginning))
               (setq p2 (region-end)))
      (progn (setq p1 (line-beginning-position))
             (setq p2 (line-beginning-position (+ arg 1)))))
    (kill-ring-save p1 p2)
    (goto-char p2)))

(global-set-key [remap kill-ring-save] 'ts-kill-ring-save)

Kill Ring Save Whole Buffer

(defun ts-kill-ring-save-whole-buffer ()
  "Save the entire contents of the buffer to the kill ring."
  (interactive)
  (kill-ring-save (point-min) (point-max))
  (message "Saved whole buffer to kill ring :-)"))

(keymap-global-set "C-c h" 'ts-kill-ring-save-whole-buffer)

Moving lines

(defmacro save-column (&rest body)
  `(let ((column (current-column)))
     (unwind-protect (progn ,@body) (move-to-column column))))
(put 'save-column 'lisp-indent-function 0)

(defun move-line-up ()
  (interactive)
  (save-column (transpose-lines 1) (forward-line -2)))

(defun move-line-down ()
  (interactive)
  (save-column (forward-line 1) (transpose-lines 1) (forward-line -1)))

(keymap-global-set "M-p" 'move-line-up)
(keymap-global-set "M-n" 'move-line-down)

XML pretty print

(defun ts-xml-pretty-print ()
  "Reformat and indent XML."
  (interactive)
  (save-excursion
    (sgml-pretty-print (point-min) (point-max))
    (indent-region (point-min) (point-max))))

File Functions

Delete this file

(defun ts-delete-this-file ()
  "Delete the current file, and kill the buffer."
  (interactive)
  (or (buffer-file-name) (error "No file is currently being edited"))
  (when (yes-or-no-p (format "Really delete '%s'?"
                             (file-name-nondirectory buffer-file-name)))
    (delete-file (buffer-file-name) t)
    (kill-this-buffer)))

(keymap-global-set "C-c f d" 'ts-delete-this-file)

Make backup of current file

(defun ts-make-backup ()
  "Make a backup copy of current file or dired marked files.

If in dired, backup current file or marked files."
  (interactive)
  (let (($fname (buffer-file-name)))
    (if $fname
        (let (($backup-name
               (concat $fname "." (format-time-string "%y%m%d%H%M") ".bak")))
          (copy-file $fname $backup-name t)
          (message (concat "Backup saved at: " $backup-name)))
      (if (string-equal major-mode "dired-mode")
          (progn
            (mapc (lambda ($x)
                    (let (($backup-name
                           (concat $x "." (format-time-string "%y%m%d%H%M") ".bak")))
                      (copy-file $x $backup-name t)))
                  (dired-get-marked-files))
            (message "marked files backed up"))
        (user-error "Buffer not file nor dired")))))
(defun ts-make-backup-and-save ()
  "Backup of current file and save, or backup dired marked files.
For detail, see `ts-make-backup'."
  (interactive)
  (if (buffer-file-name)
      (progn
        (ts-make-backup)
        (when (buffer-modified-p)
          (save-buffer)))
    (progn
      (ts-make-backup))))

(keymap-global-set "C-c f b" 'ts-make-backup-and-save)

Open current file as root

(defun ts-sudoedit (&optional arg)
  "Open current or ARG file as root."
  (interactive "P")
  (if (or arg (not buffer-file-name))
      (find-file (concat "/sudo:root@localhost:"
                         (read-file-name "Find file (as root): ")))
    (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))

(keymap-global-set "C-c f s" 'ts-sudoedit)

Yank current file contents to kill ring

Very useful for copying ssh public keys…

(defun ts-copy-file-contents-to-clipboard (file)
  "Copy a file's contents to the clipboard."
  (interactive "f")
  (with-current-buffer (find-file-noselect file)
    (kill-new (buffer-substring-no-properties (point-min) (point-max))))
  (message "Copied contents of %s to the clipboard" file))

(keymap-global-set "C-c f h" 'ts-copy-file-contents-to-clipboard)

Yank current file name to kill ring

(defun ts-copy-file-name-to-clipboard (arg)
  "Copy the current buffer file name to the clipboard.
With prefix ARG, omit path."
  (interactive "P")
  (let ((filename (if (equal major-mode 'dired-mode)
                      (if arg
                          (file-name-nondirectory
                           (directory-file-name
                            (file-name-directory default-directory)))
                        default-directory)
                    (if arg
                        (file-name-nondirectory buffer-file-name)
                      (buffer-file-name)))))
    (when filename
      (kill-new filename)
      (message "Copied '%s' to the clipboard." filename))))

(keymap-global-set "C-c f w" 'ts-copy-file-name-to-clipboard)

Window Functions

Kill buffer other window

(defun ts-kill-buffer-other-window ()
  "Kill the buffer in the last used window."
  (interactive)
  ;; Window selection is used because point goes to a different window if more
  ;; than 2 windows are present
  (let ((current-window (selected-window))
        (other-window (get-mru-window t t t)))
    (select-window other-window)
    (kill-this-buffer)
    (select-window current-window)))

(keymap-global-set "C-c w k" 'ts-kill-buffer-other-window)

Switch to the previous window

(defun ts-prev-window ()
  "Go the previously used window, excluding other frames."
  (interactive)
  (other-window -1))

(keymap-global-set "C-x O" 'ts-prev-window)

Toggle focus to last window

(defun ts-last-window ()
  "Switch back and forth between two windows easily."
  (interactive)
  (let ((win (get-mru-window t t t)))
    (unless win (error "Last window not found"))
    (let ((frame (window-frame win)))
      (raise-frame frame)
      (select-frame frame)
      (select-window win))))

(keymap-global-set "C-c w w" 'ts-last-window)

Toggle maximize window

(defun ts-toggle-maximize-window ()
  "Temporarily maximize a window."
  (interactive)
  (if (= 1 (length (window-list)))
      (jump-to-register '_)
    (progn
      (window-configuration-to-register '_)
      (delete-other-windows))))

(keymap-global-set "C-c z" 'ts-toggle-maximize-window)

Toggle vertical -> horizontal splits

(defun ts-toggle-split ()
  "Switch window split from horizontally to vertically.
Or vice versa. Change right window to bottom, or change bottom
window to right."
  (interactive)
  (autoload 'windmove-find-other-window "windmove" nil t)
  (let ((done))
    (dolist (dirs '((right . down) (down . right)))
      (unless done
        (let* ((win (selected-window))
               (nextdir (car dirs))
               (neighbour-dir (cdr dirs))
               (next-win (windmove-find-other-window nextdir win))
               (neighbour1 (windmove-find-other-window neighbour-dir win))
               (neighbour2 (if next-win
                               (with-selected-window next-win
                                 (windmove-find-other-window
                                  neighbour-dir next-win)))))
          (setq done (and (eq neighbour1 neighbour2)
                          (not (eq (minibuffer-window) next-win))))
          (if done
              (let* ((other-buf (window-buffer next-win)))
                (delete-window next-win)
                (if (eq nextdir 'right)
                    (split-window-vertically)
                  (split-window-horizontally))
                (set-window-buffer
                 (windmove-find-other-window neighbour-dir)
                 other-buf))))))))

(keymap-global-set "C-c w s" 'ts-toggle-split)

Emacs Packages

Ansi Color

(add-to-list 'comint-output-filter-functions 'ansi-color-process-output)
(defun colorize-compilation-buffer ()
  "ANSI color in compilation buffer."
  (ansi-color-apply-on-region compilation-filter-start (point)))
(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)
(add-hook 'shell-mode-hook 'ansi-color-for-comint-mode-on)

Make sure we show colors when we run commands with M-!

(defadvice display-message-or-buffer (before ansi-color activate)
  "Process ANSI color codes in shell output."
  (let ((buf (ad-get-arg 0)))
    (and (bufferp buf)
         (string= (buffer-name buf) "*Shell Command Output*")
         (with-current-buffer buf
           (ansi-color-apply-on-region (point-min) (point-max))))))

Dabbrev

(use-package dabbrev
  :config
  (setq abbrev-file-name (concat user-emacs-directory "abbrevs"))
  (setq save-abbrevs 'silently)
  (setq abbrev-suggest t)
  (setq dabbrev-check-other-buffers t)
  (setq dabbrev-eliminate-newlines t))

Dired

(use-package dired
  :custom
  (dired-omit-files "\\`[.]?#\\|\\`[.][.]?\\'\\|^\\..+$")
  (dired-dwim-target t)
  (delete-by-moving-to-trash t)
  (dired-use-ls-dired nil)
  (dired-recursive-copies 'always)
  (dired-recursive-deletes 'always)
  :config
  (defun ts-dired-get-size ()
    "Get cumlative size of marked or current item."
    (interactive)
    (let ((files (dired-get-marked-files)))
      (with-temp-buffer
        (apply 'call-process "/usr/bin/du" nil t nil "-sch" files)
        (message "Size of all marked files: %s"
                 (progn
                   (re-search-backward "\\(^[0-9.,]+[A-Za-z]+\\).*total$")
                   (match-string 1))))))

  (defun ts-dired-beginning-of-buffer ()
    "Go to first file in directory."
    (interactive)
    (goto-char (point-min))
    (dired-next-line 2))

  (defun ts-dired-end-of-buffer ()
    "Go to last file in directory."
    (interactive)
    (goto-char (point-max))
    (dired-next-line -1))

  (defun ts-dired-up-directory ()
    (interactive)
    (find-alternate-file ".."))

  (autoload 'dired-omit-mode "dired-x" nil t)
  (autoload 'dired-omit-files "dired-x" nil t)

  (when (eq system-type 'darwin) (setq dired-listing-switches "-alh"))
  (when (eq system-type 'berkeley-unix) (setq dired-listing-switches "-alhpL"))
  (when (eq system-type 'gnu/linux) (setq dired-listing-switches "-AGFhlv"))

  :bind (:map dired-mode-map
              (")" . dired-omit-mode)
              ("<" . ts-dired-beginning-of-buffer)
              (">" . ts-dired-end-of-buffer)
              ("?" . ts-dired-get-size)
              ("b" . ts-dired-up-directory)
              ("c" . dired-do-compress-to)
              ("f" . 'dired-find-alternate-file)))

Dired AUX

(use-package dired-aux
  :after dired
  :config
  (setq dired-isearch-filenames 'dwim)
  (setq dired-create-destination-dirs 'ask)
  (setq dired-vc-rename-file t))

Find Dired

(use-package find-dired
  :config
  (setq find-ls-option '("-ls" . "-AGFhlv"))
  (setq find-name-arg "-iname"))

Writeable Dired

(use-package wdired
  :config
  (setq wdired-allow-to-change-permissions t)
  (setq wdired-create-parent-directories t))

Doc View

(use-package doc-view-mode :defer 30
  :config
  (setq doc-view-continuous t)
  (setq doc-view-resolution 300))

Ediff

(use-package ediff
  :config
  (setq ediff-diff-options "-w")
  (setq ediff-keep-variants nil)
  (setq ediff-make-buffers-readonly-at-startup nil)
  (setq ediff-merge-revisions-with-ancestor t)
  (setq ediff-show-clashes-only t)
  (setq ediff-split-window-function 'split-window-horizontally)
  (setq ediff-window-setup-function 'ediff-setup-windows-plain)
  ;; https://emacs.stackexchange.com/a/24602
  (defun disable-y-or-n-p (orig-fun &rest args)
    "Advise ORIG-FUN with ARGS so it dynamically rebinds `y-or-n-p'."
    (cl-letf (((symbol-function 'y-or-n-p) (lambda () t)))
      (apply orig-fun args)))
  (advice-add 'ediff-quit :around #'disable-y-or-n-p)
  :hook
  (ediff-after-quit-hook-internal . winner-undo))

Eglot

Emacs Polyglot: an Emacs LSP client that stays out of your way.

https://github.com/joaotavora/eglot

The Language Server Protocol (LSP) defines the protocol used between an editor or IDE and a language server that provides language features like auto complete, go to definition, find all references etc. The goal of the Language Server Index Format (LSIF, pronounced like “else if”) is to support rich code navigation in development tools or a Web UI without needing a local copy of the source code.

https://microsoft.github.io/language-server-protocol/

pip install python-lsp-server
go install golang.org/x/tools/gopls@latest
npm install -g typescript-language-server typescript
npm install -g bash-language-server
npm install -g dockerfile-language-server-nodejs
(use-package eglot
  :hook
  (prog-mode . eglot-ensure)
  (shell-script-mode . eglot-ensure))

Eldoc

(add-hook 'emacs-lisp-mode-hook 'eldoc-mode)
(add-hook 'lisp-interaction-mode-hook 'eldoc-mode)
(add-hook 'lisp-mode-hook 'eldoc-mode)

Electric

(add-hook 'after-init-hook 'electric-indent-mode)
(add-hook 'after-init-hook 'electric-pair-mode)

ERC

(use-package erc
  :config
  (setq erc-autojoin-channels-alist '(("freenode.net" "#emacs")))
  (setq erc-fill-column 80)
  (setq erc-hide-list '("JOIN" "PART" "QUIT"))
  (setq erc-input-line-position -2)
  (setq erc-keywords '("knowayback"))
  (setq erc-nick "knowayback")
  (setq erc-prompt-for-password t)
  (setq erc-track-enable-keybindings t)
  :commands (erc))

Eshell

(use-package eshell
  :custom
  (shell-kill-buffer-on-exit t)
  :config
  (defun ts-eshell-prompt ()
    "Custom eshell prompt."
    (concat
     (propertize (user-login-name) 'face `(:foreground "green" ))
     (propertize "@" 'face `(:foreground "yellow"))
     (propertize (system-name) `face `(:foreground "green"))
     (propertize ":" 'face `(:foreground "yellow"))
     (if (string= (eshell/pwd) (getenv "HOME"))
         (propertize "~" 'face `(:foreground "magenta"))
       (propertize (eshell/basename (eshell/pwd)) 'face `(:foreground "magenta")))
     "\n"
     (if (= (user-uid) 0)
         (propertize "#" 'face `(:foreground "red"))
       (propertize "$" 'face `(:foreground "yellow")))
     (propertize " " 'face `(:foreground "white"))))
  ;; https://www.emacswiki.org/emacs/EshellPrompt
  (setq eshell-cd-on-directory t
        eshell-destroy-buffer-when-process-dies t
        eshell-highlight-prompt nil
        eshell-hist-ignoredups t
        eshell-history-size 4096
        eshell-ls-use-colors t
        eshell-prefer-lisp-functions t
        eshell-prefer-lisp-variables t
        eshell-prompt-regexp "^[^#$\n]*[#$] "
        eshell-prompt-function 'ts-eshell-prompt
        eshell-review-quick-commands nil
        eshell-save-history-on-exit t
        eshell-smart-space-goes-to-end t
        eshell-where-to-jump 'begin)
  (add-to-list 'eshell-modules-list 'eshell-tramp)
  :bind
  ("C-c e" . eshell)
  :hook
  (eshell-preoutput-filter-functions . ansi-color-apply))

Flymake

(use-package flymake-mode
  :hook
  (prog-mode . flymake-mode)
  (json-mode . flymake-mode)
  (yaml-mode . flymake-mode)
  (shell-script-mode . flymake-mode))

Flyspell

Don’t bother using this on Windows as I can rather be bothered to install Aspell for Windows.

(unless (eq system-type 'windows-nt)
  (add-hook 'text-mode-hook 'flyspell-mode)
  (add-hook 'org-mode-hook 'flyspell-mode)
  (add-hook 'prog-mode-hook 'flyspell-prog-mode))

Hide/Show

(add-hook 'shell-script-mode-hook 'hs-minor-mode)
(add-hook 'prog-mode-hook 'hs-minor-mode)

Highlight line

(add-hook 'dired-mode-hook 'hl-line-mode)
(add-hook 'org-mode-hook 'hl-line-mode)
(add-hook 'prog-mode-hook 'hl-line-mode)
(add-hook 'shell-script-mode-hook 'hl-line-mode)
(add-hook 'text-mode-hook 'hl-line-mode)

Hippie Expand

(defun ts-hippie-expand-completions (&optional hippie-expand-function)
  "Return the full list of completions generated by HIPPIE-EXPAND-FUNCTION.
The optional argument can be generated with `make-hippie-expand-function'."
  (let ((this-command 'ts-hippie-expand-completions)
        (last-command last-command)
        (buffer-modified (buffer-modified-p))
        (hippie-expand-function (or hippie-expand-function 'hippie-expand)))
    (cl-flet ((ding)) ; avoid the (ding) when hippie-expand exhausts its options.
      (while (progn
               (funcall hippie-expand-function nil)
               (setq last-command 'ts-hippie-expand-completions)
               (not (equal he-num -1)))))
    ;; Evaluating the completions modifies the buffer, however we will finish
    ;; up in the same state that we began.
    (set-buffer-modified-p buffer-modified)
    ;; Provide the options in the order in which they are normally generated.
    (delete he-search-string (reverse he-tried-table))))

(defun ts-hippie-complete-with (hippie-expand-function)
  "Offer `completing-read' using the specified HIPPIE-EXPAND-FUNCTION."
  (let* ((options (ts-hippie-expand-completions hippie-expand-function))
         (selection (and options (completing-read "Completions: " options))))
    (if selection
        (he-substitute-string selection t)
      (message "No expansion found"))))

(defun ts-hippie-expand-completing-read ()
  "Offer `completing-read' for the word at point."
  (interactive)
  (ts-hippie-complete-with 'hippie-expand))

(keymap-global-set "C-M-/" 'hippie-expand)
(keymap-global-set "M-/" 'ts-hippie-expand-completing-read)

Icomplete

(use-package icomplete
  :custom
  (fido-vertical-mode t)
  :config
  (defun ts-icomplete-styles ()
    (setq-local completion-styles '(initials flex partial-completion substring basic)))
  :hook
  (icomplete-minibuffer-setup . ts-icomplete-styles)
  :bind (:map icomplete-fido-mode-map
              ("C-<tab>" . icomplete-force-complete-and-exit)
              ("M-j" . exit-minibuffer)))

Imenu

(use-package imenu
  :config
  (setq imenu-auto-rescan t)
  (setq imenu-auto-rescan-maxout 600000)
  (setq imenu-eager-completion-buffer t)
  (setq imenu-level-separator "/")
  (setq imenu-max-item-length 100)
  (setq imenu-space-replacement " ")
  (setq imenu-use-markers t)
  (setq imenu-use-popup-menu nil)
  :bind
  ("C-c i" . imenu))

Line Numbers

(setq display-line-numbers 'relative)
(add-hook 'prog-mode-hook 'display-line-numbers-mode)
(add-hook 'sh-script-hook 'display-line-numbers-mode)

Occur

(add-hook 'occur-mode-hook 'hl-line-mode)
(keymap-set occur-mode-map "t" 'toggle-truncate-lines)

Org

(use-package org
  :custom
  (org-latex-listings 'minted)
  (org-latex-pdf-process
   '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
     "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
  (org-startup-folded t) ; start in overview mode
  (org-directory "~")
  (org-agenda-files (file-expand-wildcards "~/**/*.org"))
  (org-default-notes-file "~/notes.org")
  (org-image-actual-width nil) ; try to get width from #+ATTR.* keyword
  (setf org-blank-before-new-entry '((heading . nil) (plain-list-item . nil)))
  (org-emphasis-regexp-components '(" \t('\"{" "- \t.,:!?;'\")}\\" " \t\r\n,\"'" "." 300))
  (org-confirm-babel-evaluate 'nil)
  (org-export-with-toc t)
  (org-indent-indentation-per-level 1)
  (org-list-allow-alphabetical t)
  (org-list-indent-offset 1)
  (org-return-follows-link t)
  (org-startup-indented t)
  (org-html-validation-link nil) ; don't add "validate" postamble
  (org-use-fast-todo-selection t)
  (org-goto-interface 'outline-path-completionp)
  (org-outline-path-complete-in-steps nil)
  (org-refile-use-outline-path 'file)
  (org-refile-allow-creating-parent-nodes 'confirm)
  (org-refile-targets '((nil :maxlevel . 9)))
  (org-special-ctrl-a/e 'reversed)
  (org-special-ctrl-k t)
  (org-special-ctrl-o t)
  (org-use-speed-commands t)
  (org-speed-commands-user '(("N" . org-down-element) ("P" . org-up-element)))
  (org-src-fontify-natively t)
  (org-src-tab-acts-natively t)
  (org-src-window-setup 'current-window)
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((awk . t)
     (C . t)
     (css . t)
     (emacs-lisp . t)
     (lisp . t)
     (makefile . t)
     (perl . t)
     (python . t)
     (ruby . t)
     (sed . t)
     (shell . t)
     (sql . t)
     (sqlite . t)))
  (add-to-list 'org-latex-packages-alist '("" "minted"))
  (add-to-list 'org-modules 'org-tempo t)
  (setq org-structure-template-alist
        (append org-structure-template-alist
                '(("cl" . "src common-lisp")
                  ("el" . "src emacs-lisp")
                  ("go" . "src go")
                  ("ja" . "src java")
                  ("js" . "src javascript")
                  ("kr" . "src c")
                  ("py" . "src python")
                  ("sh" . "src shell")
                  ("sq" . "src sql")
                  ("tx" . "src text"))))
  :bind (:map org-mode-map ("C-c i" . org-goto))
  :hook (org-babel-post-tangle . emacs-lisp-byte-compile-and-load))

Pending delete

(add-hook 'after-init-hook 'pending-delete-mode 1) ;; remove selected region if typing

Prettify Symbols

(use-package prettify-symbols
  :custom (prettify-symbols-unprettify-at-point 'right-edge)
  :hook (after-init . global-prettify-symbols-mode))

Project

(use-package project
  :custom
  (project-switch-commands
   '((?b "Buffer" project-switch-to-buffer)
     (?c "Compile" project-compile)
     (?d "Dired" project-dired)
     (?e "Eshell" project-eshell)
     (?f "File" project-find-file)
     (?g "Grep" project-find-regexp)
     (?q "Query replace" project-query-replace-regexp)
     (?r "Run command" project-async-shell-command)
     (?s "Search" project-search)
     (?v "VC dir" project-vc-dir)))
  :config
  (setq ts-project-roots '("~/src" "~/oe-developers"))
  (defun ts-project--git-repo-p (directory)
    "Return non-nil if there is a git repository in DIRECTORY."
    (and
     (file-directory-p (concat directory "/.git"))
     (file-directory-p (concat directory "/.git/info"))
     (file-directory-p (concat directory "/.git/objects"))
     (file-directory-p (concat directory "/.git/refs"))
     (file-regular-p (concat directory "/.git/HEAD"))))

  (defun ts-project--git-repos-recursive (directory maxdepth)
    "List git repos in under DIRECTORY recursively to MAXDEPTH."
    (let* ((git-repos '())
           (current-directory-list
            (directory-files directory t directory-files-no-dot-files-regexp)))
      ;; while we are in the current directory
      (if (ts-project--git-repo-p directory)
          (setq git-repos (cons (file-truename (expand-file-name directory)) git-repos)))
      (while current-directory-list
        (let ((f (car current-directory-list)))
          (cond ((and (file-directory-p f)
                      (file-readable-p f)
                      (> maxdepth 0)
                      (not (ts-project--git-repo-p f)))
                 (setq git-repos
                       (append git-repos
                               (ts-project--git-repos-recursive f (- maxdepth 1)))))
                ((ts-project--git-repo-p f)
                 (setq git-repos (cons
                                  (file-truename (expand-file-name f)) git-repos))))
          (setq current-directory-list (cdr current-directory-list))))
      (delete-dups git-repos)))

  (defun ts-project--list-projects ()
    "Produce list of projects in `ts-project-roots'."
    (let ((cands (delete-dups (mapcan (lambda (directory)
                                        (ts-project--git-repos-recursive
                                         (expand-file-name directory)
                                         10))
                                      ts-project-roots))))
      ;; needs to be a list of lists
      (mapcar (lambda (d)
                (list (abbreviate-file-name d)))
              cands)))

  (defun ts-project-update-projects ()
    "Overwrite `project--list' using `ts-project--list-projects'.
      WARNING: This will destroy & replace the contents of `project-list-file'."
    (interactive)
    (autoload 'project--ensure-read-project-list "project" nil t)
    (project--ensure-read-project-list)
    (setq project--list (ts-project--list-projects))
    (project--write-project-list)
    (message "Updated project list in %s" project-list-file))
  :bind
  ("C-x pu" . ts-project-update-projects))

Python

(use-package python :custom (python-fill-docstring-style 'django))

Recentf

(use-package recentf
  :custom
  (recentf-exclude '(".gz"
                     ".xz"
                     ".zip"
                     "/elpa/"
                     "/ssh:"
                     "/sudo:"
                     "^/var/folders\\.*"
                     "COMMIT_EDITMSG\\'"
                     ".*-autoloads\\.el\\'"
                     "[/\\]\\.elpa/"))
  (recentf-max-menu-items 128)
  (recentf-max-saved-items 256)
  :config
  (defun ts--recentf-get-paths ()
    "Return de-duplicated and abbreviated `recentf-list'."
    (delete-dups (mapcar 'abbreviate-file-name recentf-list)))
  (defun ts-recentf-find-file ()
    "`find-file' using `recentf-list'."
    (interactive)
    (find-file (completing-read "Find file: " (ts--recentf-get-paths))))
  (defun ts-recentf-find-file-other-window ()
    "`find-file' using `recentf-list'."
    (interactive)
    (find-file-other-window
     (completing-read "Find file in other window: " (ts--recentf-get-paths))))
  (defun ts-recentf-find-file-other-frame ()
    "`find-file' using `recentf-list'."
    (interactive)
    (find-file-other-frame
     (completing-read "Find file in other frame: " (ts--recentf-get-paths))))
  :bind
  ("C-c r" . ts-recentf-find-file)
  ("C-c 4 r" . ts-recentf-find-file-other-window)
  ("C-c 5 r" . ts-recentf-find-file-other-frame)
  ("C-c C-r" . recentf-open-files)
  :hook
  (after-init . recentf-mode))

Save history mode

(use-package savehist
  :config
  (setq savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
  (setq savehist-save-minibuffer-history 1)
  :hook (after-init . savehist-mode))

Saveplace

(use-package save-place
  :custom (save-place-file (concat user-emacs-directory "saveplace.el"))
  :hook (after-init . save-place-mode))

Semantic

;; (add-hook 'prog-mode-hook 'semantic-mode)
;; https://github.com/abo-abo/lispy/issues/473
(advice-add 'semantic-idle-scheduler-function :around #'ignore)

Shell Script

(use-package sh-script
  :mode
  ("\\.sh\\'" . shell-script-mode)
  ("\\.bash.*\\'" . shell-script-mode)
  ("\\.zsh.*\\'" . shell-script-mode)
  ("\\.aliases\\'" . shell-script-mode)
  ("\\.functions\\'" . shell-script-mode)
  ("\\bashrc\\'" . shell-script-mode)
  ("\\kshrc\\'" . shell-script-mode)
  ("\\profile\\'" . shell-script-mode)
  ("\\zshenv\\'" . shell-script-mode)
  ("\\zprompt\\'" . shell-script-mode)
  ("\\zshrc\\'" . shell-script-mode)
  ("\\prompt_.*_setup\\'" . shell-script-mode)
  :interpreter
  ("bash" . shell-script-mode)
  ("ksh" . shell-script-mode)
  ("sh" . shell-script-mode)
  ("zsh" . shell-script-mode)
  :hook
  (after-save . executable-make-buffer-file-executable-if-script-p))

Show paren

(use-package paren
  :custom
  (show-paren-when-point-in-periphery t)
  (show-paren-when-point-inside-paren t)
  :hook (after-init . show-paren-mode))

Subword

(add-hook 'after-init-hook 'global-subword-mode) ;; move by camel case, etc

Term

(use-package term
  :config
  (defadvice term-handle-exit (after term-kill-buffer-on-exit activate)
    "Kill term when shell exits."
    (kill-buffer))
  (setq term-buffer-maximum-size 200000)
  :bind ("C-c t" . ansi-term))

Tramp

(with-eval-after-load 'tramp
  (setq tramp-backup-directory-alist backup-directory-alist)
  (setq tramp-default-method "ssh")
  (setf tramp-persistency-file-name (concat temporary-file-directory "tramp-" (user-login-name)))
  (message "Lazy loaded tramp :-)"))

Version Control

Protesilaos Stavrou’s VC reference

(use-package vc
  :custom
  (vc-follow-symlinks t)
  (vc-make-backup-files t)
  (version-control t)
  :bind ("C-x v d" . vc-dir-root))

Whitespace

(use-package whitespace
  :custom
  (whitespace-line-column 120)
  (whitespace-style '(face
                      tabs
                      spaces
                      trailing
                      lines
                      space-before-tab::space
                      newline
                      indentation::space
                      empty
                      space-after-tab::space
                      space-mark
                      tab-mark
                      newline-mark)
                    whitespace-face 'whitespace-trailing)
  :bind ("C-c M-w" . whitespace-mode)
  :hook (before-save . whitespace-cleanup))

Windmove

(use-package windmove
  :custom (windmove-wrap-around t)
  :bind
  ("C-c w b" . windmove-left)
  ("C-c w f" . windmove-right)
  ("C-c w p" . windmove-up)
  ("C-c w n" . windmove-down)
  ("C-c w C-b" . windmove-swap-states-left)
  ("C-c w C-f" . windmove-swap-states-right)
  ("C-c w C-p" . windmove-swap-states-up)
  ("C-c w C-n" . windmove-swap-states-down))

Window Divider

(setq window-divider-default-right-width 1)
(setq window-divider-default-bottom-width 1)
(setq window-divider-default-places 'right-only)
(add-hook 'window-setup-hook 'window-divider-mode)

Winner mode

Startup Summary

(add-hook 'window-setup-hook 'winner-mode)
(keymap-global-set "C-c w u" 'winner-undo)
(keymap-global-set "C-c w r" 'winner-redo)

MELPA Packages

I like to split up my “vanilla” Emacs configuration and custom functions from the customisations provided by third party libraries.

If you want to go for a more vanilla setup, just prefix the COMMENT keyword to the tile above (or press C-c ; whilst under this heading).

Then, re-tangle and re-load the file, by simply saving it.

This way it makes it very easy to run a semi-stock Emacs without package.el or any third party libraries.

Yet still have some saner defaults and some extra functionality.

;; Allow loading from the package cache.
(setq package-quickstart t)
;; Don't write (package-initialize) to my init file!
(setq package--init-file-ensured t)
;; Turn on ahead-of-time native compilation when installing a package.
(setq package-native-compile t)
;; Setup up archives
(setq package-archives
      '(("gnu" . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")
        ("nongnu" . "https://elpa.nongnu.org/nongnu/")))

Ansible

(use-package ansible :ensure :hook (yaml-mode . ansible))
(use-package ansible-doc :ensure :hook (yaml-mode . ansible-doc-mode))

Async

(use-package async :ensure t)

(use-package dired-async :after (dired async)
  :hook (dired-mode . dired-async-mode))

Blacken

(use-package blacken :ensure :hook (python-mode . blacken-mode))

Blamer

(use-package blamer :ensure :defer 20
  :custom
  (blamer-idle-time 0.5)
  (blamer-min-offset 20)
  (blamer-max-commit-message-length 50)
  :config (global-blamer-mode 1))

Corfu

(use-package corfu :ensure
  :custom
  (corfu-cycle t)
  (corfu-auto t)
  :hook
  (prog-mode . corfu-mode)
  (shell-mode . corfu-mode)
  (eshell-mode . corfu-mode))

Default Text Scale

Increase or decrease font size everywhere with C-M-= and C-M--

https://github.com/purcell/default-text-scale

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

Docker

(use-package dockerfile-mode :ensure)

Exec path from shell

Don’t tangle this block if system-type is windows-nt.

(use-package exec-path-from-shell :ensure :defer 10
  :unless (eq system-type 'windows-nt)
  :commands exec-path-from-shell-initialize
  :init
  (setq exec-path-from-shell-check-startup-files 'nil)
  :config
  (exec-path-from-shell-initialize)
  (exec-path-from-shell-copy-env "DEVPATH")
  (exec-path-from-shell-copy-env "PYTHONPATH"))

Flycheck

(use-package flycheck :ensure
  :unless (eq system-type 'windows-nt)
  :diminish flycheck-mode
  :config
  (flycheck-add-mode 'javascript-eslint 'web-mode)
  :hook
  (prog-mode . flycheck-mode)
  (shell-script-mode . flycheck-mode))

Git

Gitlab CI

(use-package gitlab-ci-mode :ensure
  :mode
  "\\.gitlab-ci.yaml\\'"
  "\\.gitlab-ci.yml\\'"
  :hook
  (yaml-mode . hs-minor-mode))

Git Timemachine

(use-package git-timemachine :ensure)

Magit

(use-package magit :ensure
  :bind*
  ("C-x g" . magit-status)
  :config
  (when (eq system-type 'windows-nt)
    (if (file-readable-p "C:/Program Files/Git/bin/git.exe")
        (setq magit-git-executable "C:/Program Files/Git/bin/git.exe"))
    (when (file-directory-p "C:/Program Files/Git/bin")
      (setq exec-path (add-to-list 'exec-path "C:/Program Files/Git/bin"))
      (setenv "PATH" (concat "C:\\Program Files\\Git\\bin;" (getenv "PATH")))))
  (setq magit-clone-set-remote.pushDefault t)
  (setq magit-completing-read-function 'magit-builtin-completing-read))
(use-package magit-repos
  :bind* ("C-x C-g" . magit-list-repositories)
  :config
  (setq magit-repository-directories `(("~/" . 0)
                                       ("~/src" . 10)
                                       ("~/oe-developers" . 10)))
  (setq magit-repolist-columns
        '(("Flag" 5 magit-repolist-column-flags ((:right-align t) nil))
          ("Name" 20 magit-repolist-column-ident nil)
          ("Branch" 10 magit-repolist-column-branch nil)
          ;; ("Version" 25 magit-repolist-column-version nil)
          ("Pull" 5 magit-repolist-column-unpulled-from-upstream ((:right-align t) nil))
          ("Push" 5 magit-repolist-column-unpushed-to-upstream ((:right-align t) nil))
          ;; ("Pull" 5 magit-repolist-column-unpulled-from-pushremote ((:right-align t) nil))
          ;; ("Push" 5 magit-repolist-column-unpushed-to-pushremote ((:right-align t) nil))
          ("Path" 99 magit-repolist-column-path nil)))
  (setq magit-repolist-sort-key '("Flag" . t)))

Go mode

(use-package go-mode :ensure
  :config
  (defun ts-go-indent ()
    (setq indent-tabs-mode 1)
    (setq tab-width 2))
  :hook
  (go-mode . ts-go-indent)
  (before-save . gofmt-before-save))

Hungry delete

(use-package hungry-delete :ensure :defer 6 :config (global-hungry-delete-mode))

JSON

(use-package json-mode :ensure
  :config
  (defun ts-json-mode-setup ()
    (json-mode)
    (json-pretty-print (point-min) (point-max))
    (goto-char (point-min))
    (set-buffer-modified-p nil))
  (add-to-list 'auto-mode-alist
               '("\\.json\\'" . 'ts-json-mode-setup)))

Markdown

(use-package markdown-mode :ensure
  :commands (markdown-mode gfm-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md\\'" . gfm-mode)
         ("\\.markdown\\'" . gfm-mode))
  :init (setq markdown-command "multimarkdown"))

Nix

(use-package nix-mode :ensure)
(use-package nixpkgs-fmt :ensure :hook (nix-mode . nixpkgs-fmt-on-save))

Org

(use-package htmlize :ensure)

Powershell

(use-package powershell :ensure :mode (("\\.ps1\\'" . powershell-mode)))

Restclient

(use-package restclient :ensure)

systemd

Don’t tangle this block if system-type is windows-nt.

(use-package systemd :ensure :unless (equal system-type 'windows-nt))

Terraform

(use-package terraform-mode :ensure)

Trashed

(use-package trashed :ensure :bind ("C-c f t" . trashed))

Web Mode

(use-package web-mode :ensure
  :mode
  "\\.phtml\\'"
  "\\.tpl\\.php\\'"
  "\\.[agj]sp\\'"
  "\\.as[cp]x\\'"
  "\\.erb\\'"
  "\\.mustache\\'"
  "\\.djhtml\\'"
  "\\.html\\.twig\\'"
  "\\.html?\\'"
  "\\.php?\\'"
  "\\.css?\\'"
  :hook (web-mode . js2-minor-mode)
  :custom
  (web-mode-code-indent-offset 2)
  (web-mode-markup-indent-offset 2)
  (web-mode-attr-indent-offset 2)
  (web-mode-css-indent-offset 2)
  (web-mode-code-indent-offset 2)
  (web-mode-enable-auto-pairing t))

Which Key

(use-package which-key :ensure :defer 5 :config (which-key-mode))

YAML

(use-package highlight-indentation :ensure)
(use-package yaml-mode :ensure
  :hook
  (yaml-mode . highlight-indentation-mode)
  (yaml-mode . hs-minor-mode)
  (yaml-mode . display-line-numbers-mode))

Yasnippet

(use-package yasnippet :ensure :hook (prog-mode . yas-minor-mode))
(use-package yasnippet-classic-snippets :ensure)
(use-package yasnippet-snippets :ensure)