Skip to content
This repository has been archived by the owner on Apr 21, 2021. It is now read-only.

svend/dot-emacsd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs Configuration

User information

(setq user-full-name "Svend Sorensen")
(setq user-mail-address "[email protected]")

Initialize package.el

Emacs Package Installation

(setq package-archives '()) ;; All packages are installed through nixpkgs
(package-initialize)

Initialize use-package

This is required before any use-package forms.

(eval-when-compile
  (require 'use-package))
(require 'diminish)
(require 'bind-key)

Initialize exec-path-from-shell

Function that gets environmental variables from bash. This uses printenv from nixpkgs on macOS.

(defun bash-shell-variables()
  "Return a list of env variable names."
  (mapcar (lambda (s) (car (split-string (string-remove-prefix "declare -x " s) "=")))
          (split-string
           (shell-command-to-string "bash -l -c \"declare -x\" 2>/dev/null")
           "\n" t)))

(defun my-exec-path-from-shell-initialize ()
  "Initialize environment with all shell variables."
  (interactive)
  (exec-path-from-shell-copy-envs (bash-shell-variables)))

This needs to come before anything that uses PATH (e.g. executable-find).

(use-package exec-path-from-shell
  :if window-system
  :init (setq exec-path-from-shell-variables (bash-shell-variables))
  :config (exec-path-from-shell-initialize))

Emacs UI

Make the cursor a bar instead of a filled box.

(setq-default cursor-type 'bar)

Chrome

  • Disable welcome screen
  • Disable menu bar
  • Disable tool bar
(setq inhibit-splash-screen t)
(if (fboundp 'menu-bar-mode) (menu-bar-mode -1))
(if (fboundp 'tool-bar-mode) (tool-bar-mode -1))

Mode line

  • Display column number in mode-line (line number is displayed by default)
(column-number-mode t)

Scrolling

  • Enable smooth scrolling.
(pixel-scroll-mode)

Fonts

Line spacing

Add additional spacing between lines.

(setq-default line-spacing 0.15)

To see how different values look, press C-x C-e at the end of these lines:

(setq line-spacing nil)
(setq line-spacing 0.05)
(setq line-spacing 0.10)
(setq line-spacing 0.15)
(setq line-spacing 0.20)

Linux

I install fonts through nixpkgs.

User fonts can also go in $XDG_DATA_HOME/fonts/ (~/.local/share/fonts).

macOS

On macOS, I install fonts through nixpkgs, then sync them using a script.

#!/bin/sh
set -e

FONT_DIR="$HOME"/Library/Fonts

[ -d "$FONT_DIR" ] || exit 1

rsync -av \
  --copy-links \
  --delete \
  "$HOME"/.nix-profile/share/fonts/*/ \
  "$HOME"/Library/Fonts

Fixed pitch mode

(require 'face-remap)

(defun my-fixed-pitch-mode (&optional arg)
  "Fixed-pitch default-face mode.
  An interface to `buffer-face-mode' which uses the `fixed-pitch' face.
  Besides the choice of face, it is the same as `buffer-face-mode'."
  (interactive (list (or current-prefix-arg 'toggle)))
  (buffer-face-mode-invoke 'fixed-pitch arg
                           (called-interactively-p 'interactive)))

;; Remove BufFace from mode line
(eval-after-load "face-remap"
  '(diminish 'buffer-face-mode))

Disable pager

Set PAGER to cat to disable less in async buffers.

(setenv "PAGER" "cat")

Emacs themes

Disable current theme before loading new theme. This prevents artifacts from the old theme.

Emacs disable-theme after loading a different one (Stack Overflow)

(defun disable-all-themes ()
  "Disable all active themes."
  (interactive)
  (dolist (i custom-enabled-themes)
    (disable-theme i)))

(defun my-load-theme ()
  "Load a single theme then load override theme."
  (interactive)
  (disable-all-themes)
  (call-interactively 'load-theme)
  (load-theme 'svend t)
  (load-theme 'svend-font-dejavu t))

Treat all themes as safe.

(setq custom-safe-themes t)
(setq custom-theme-directory "~/.emacs.d/conf/")

Load themes.

(load-theme 'tango-plus)
(load-theme 'svend 't)
(load-theme 'svend-font-dejavu t)

Emacs settings

Bell

Use visual bell.

(setq visible-bell t)

Reduce bell noise for common actions (e.g. C-g).

(setq ring-bell-function
      (lambda ()
        (unless
            (memq this-command
                  '(abort-recursive-edit
                    isearch-abort
                    isearch-printing-char
                    keyboard-quit
                    nil))
          (ding))))

Mouse

Copy test selected by the mouse to the kill ring. This was turned off in Emacs 24.

(setq mouse-drag-copy-region t)

Highlight current line

Highlight the current line, including in inactive windows.

(setq global-hl-line-sticky-flag t)
(global-hl-line-mode t)

To disable for a mode, add this to the mode hook:

(setq-local global-hl-line-mode nil)

Y/N answers

Enable y/n answers.

(fset 'yes-or-no-p 'y-or-n-p)

Disabled commands

Enable some disabled commands.

(mapc (lambda (command) (put command 'disabled nil))
      '(erase-buffer
        downcase-region
        upcase-region
        upcase-initials-region))

Disable keyboard shortcut to print buffer.

(global-unset-key (kbd "s-p"))

Enable delete-selection-mode

This replaces the selection.

(delete-selection-mode)

macOS specific configuration

(when (eq window-system 'ns)
  ;; Stop commant+t from popping up font selection window
  (global-unset-key (kbd "s-t"))
  ;; Menu bar auto-hides on macOS
  (menu-bar-mode 1)
  ;; Unset TERM_PROGRAM=Apple_Terminal, which will be set if GUI Emacs was
  ;; launched from a terminal
  (setenv "TERM_PROGRAM" nil))

Programming modes

Turn on flyspell and goto-address for all text buffers.

(add-hook 'text-mode-hook #'flyspell-mode)
(add-hook 'text-mode-hook #'goto-address-mode)
(add-hook 'text-mode-hook #'variable-pitch-mode)

Turn on flyspell, goto-address, line numbers, and whitespace for programming buffers.

(defun my-prog-mode-hook()
  (flyspell-prog-mode)
  (goto-address-prog-mode)
  (setq display-line-numbers t
        show-trailing-whitespace t
        truncate-lines t))

(add-hook 'prog-mode-hook #'my-prog-mode-hook)
(add-hook 'yaml-mode-hook #'my-prog-mode-hook)
(add-hook 'yaml-mode-hook #'my-fixed-pitch-mode)
(global-eldoc-mode)

Auto modes

bash-fc-* are bash command editing temporary files (fc built-in).

(add-to-list 'auto-mode-alist '(".mrconfig$" . conf-mode))
(add-to-list 'auto-mode-alist '("/etc/network/interfaces" . conf-mode))
(add-to-list 'auto-mode-alist '("Carton\\'" . lisp-mode))
(add-to-list 'auto-mode-alist '("bash-fc-" . sh-mode))

Lock files

https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Locks.html

Locks are created in the same directory as the file being edited. They can be disabled as of 24.3.

https://lists.gnu.org/archive/html/emacs-devel/2011-07/msg01020.html

(setq create-lockfiles nil)

Backup and auto-saves

Put all backup and auto-saves into ~/.emacs.d instead of the current directory.

(setq backup-directory-alist
      `((".*" . ,(expand-file-name "backup/" user-emacs-directory))))
(setq auto-save-file-name-transforms
      `((".*" ,(expand-file-name "backup/" user-emacs-directory) t)))

Revert

Enable global auto-revert mode.

(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)

Key bindings

C-c letter and <F5> through <F9> are reserved for user use. Press C-c C-h to show all of these.

(bind-key "C-c b" 'browse-url-at-point)
(bind-key "C-c d" 'my-insert-date)
(bind-key "C-c e" 'eww)
(bind-key "C-c j" 'dired-jump)
(bind-key "C-c r" 'revert-buffer)

Space as control key

Use space as control key using xcape on Linux and Karabiner on macOS.

xcape:

# Map an unused modifier's keysym to the spacebar's keycode and make
# it a control modifier. It needs to be an existing key so that emacs
# won't spazz out when you press it. Hyper_L is a good candidate.
spare_modifier="Hyper_L"
xmodmap -e "keycode 65 = $spare_modifier"
xmodmap -e "remove mod4 = $spare_modifier" # hyper_l is mod4 by default
xmodmap -e "add Control = $spare_modifier"

# Map space to an unused keycode (to keep it around for xcape to use).
xmodmap -e "keycode any = space"

# Finally use xcape to cause the space bar to generate a space when
# tapped.
xcape -e "$spare_modifier=space"

Karabiner:

  • Change Space Key
  • Space to Control_L (+ When you type Space only, send Space)
(bind-key "C-x M-SPC" 'pop-global-mark)
(bind-key "M-SPC" 'set-mark-command)
(bind-key "M-s-SPC" 'mark-sexp)
(bind-key "M-s- " 'mark-sexp)           ; macOS
(bind-key "s-SPC" 'just-one-space)

Other settings

Rapid mark-pop (C-u C-SPC C-SPC...).

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

Shorter auto-revert interval. Default is 5 seconds.

(setq auto-revert-interval 1)

Misc settings.

(setq confirm-kill-processes nil)
(setq enable-local-variables :safe)
(setq history-length 10000)
(setq require-final-newline t) ;; Add final newline when saving
(setq save-interprogram-paste-before-kill t) ;; Do not clobber text copied from the clipboard
(setq sentence-end-double-space nil)
(setq-default indent-tabs-mode nil)
(show-paren-mode)

Pulled these from emacs-custom.el.

(setq ediff-split-window-function (quote split-window-horizontally))
(setq ediff-window-setup-function (quote ediff-setup-windows-plain))
;; (setq ffap-machine-p-known 'reject)
;; (setq ns-pop-up-frames nil)
(setq reb-re-syntax 'string)

Wrap lines at N columns instead of 70.

(setq-default fill-column 80)

Set timezones for display-time-world.

(setq zoneinfo-style-world-list
      '(("UTC" "UTC")
        ("America/Los_Angeles" "Seattle")
        ("Europe/Budapest" "Budapest")))

Prefer newer files.

(setq load-prefer-newer t)

Add options to kill or revert buffer when prompting to save modified buffers.

(add-to-list
 'save-some-buffers-action-alist
 '(?k
   (lambda (buf)
     (kill-buffer buf))
   "kill this buffer"))

(add-to-list
 'save-some-buffers-action-alist
 '(?r
   (lambda (buf)
     (save-current-buffer
       (set-buffer buf)
       (revert-buffer t t t)))
   "revert this buffer"))

Compile

(setq compilation-scroll-output 'first-error)
(defun my-colorize-compilation-buffer ()
  "Colorize a compilation mode buffer."
  ;; we don't want to mess with child modes such as grep-mode, ack, ag, etc
  (when (eq major-mode 'compilation-mode)
    (let ((inhibit-read-only t))
      (ansi-color-apply-on-region (point-min) (point-max)))))

;; Colorize output of Compilation Mode, see
;; https://stackoverflow.com/a/3072831/355252
(require 'ansi-color)
(add-hook 'compilation-filter-hook #'my-colorize-compilation-buffer)

Scratch buffer

(setq initial-major-mode 'fundamental-mode)
(setq initial-scratch-message "Scratch Buffer\n\n")

User defined functions

Hacked version of balance-windows which only balances windows horizontally.

(defun balance-windows-horizontally (&optional window-or-frame)
  "Horizontally balance the sizes of windows of WINDOW-OR-FRAME.
  WINDOW-OR-FRAME is optional and defaults to the selected frame.
  If WINDOW-OR-FRAME denotes a frame, balance the sizes of all
  windows of that frame.  If WINDOW-OR-FRAME denotes a window,
  recursively balance the sizes of all child windows of that
  window."
  (interactive)
  (let* ((window
          (cond
           ((or (not window-or-frame)
                (frame-live-p window-or-frame))
            (frame-root-window window-or-frame))
           ((or (window-live-p window-or-frame)
                (window-child window-or-frame))
            window-or-frame)
           (t
            (error "Not a window or frame %s" window-or-frame))))
         (frame (window-frame window)))
    ;; ;; Balance vertically.
    ;; (window--resize-reset (window-frame window))
    ;; (balance-windows-1 window)
    ;; (when (window--resize-apply-p frame)
    ;;   (window-resize-apply frame)
    ;;   (window--pixel-to-total frame)
    ;;   (run-window-configuration-change-hook frame))
    ;; Balance horizontally.
    (window--resize-reset (window-frame window) t)
    (balance-windows-1 window t)
    (when (window--resize-apply-p frame t)
      (window-resize-apply frame t)
      (window--pixel-to-total frame t)
      (run-window-configuration-change-hook frame))))
(defun my-toggle-line-numbers()
  (interactive)
  (call-interactively #'display-line-numbers-mode))
(defun my-save-buffer-to-clipboard()
  "Save contents of buffer to clipboard."
  (interactive)
  (clipboard-kill-ring-save (point-min) (point-max)))

(define-minor-mode my-edit-clipboard-mode
  "Minor mode for my-edit-edit-keyboard."
  :init-value nil
  :keymap
  `((,(kbd "C-c C-c") . my-save-buffer-to-clipboard)))

(defun my-edit-clipboard()
  "Switch to a buffer that contains the contents of the clipboard."
  (interactive)
  (let ((buf (generate-new-buffer "*clipboard*")))
    (switch-to-buffer buf)
    (clipboard-yank)
    (my-edit-clipboard-mode)))
(defun my-shell-cd ()
  "Switch to shell buffer and change directory to `default-directory'."
  (interactive)
  (let ((d default-directory))
    (shell)
    (goto-char (point-max))
    (insert (format "cd %s" d))
    (comint-send-input)))
(defun my-insert-date (arg)
  "Insert date string"
  (interactive "p")
  (cond ((= arg 1)
         (insert (format-time-string "%F")))
        ((= arg 4)
         (insert (format-time-string "%F-%H%M%S")))))

Packages

ace-link

(use-package ace-link
  :init (ace-link-setup-default))

ace-window

(use-package ace-window
  :bind (("C-x o" . ace-window)))

aggressive-indent

(use-package aggressive-indent
  :init
  (global-aggressive-indent-mode 1)
  :config
  (add-to-list 'aggressive-indent-excluded-modes 'nix-mode)
  ;; jsonnet-mode's formatting differs from jsonnetfmt command
  (add-to-list 'aggressive-indent-excluded-modes 'jsonnet-mode))

alert

(use-package alert
  :defer t
  :init
  (defun comint-alert-on-prompt (string)
    "Send alert when prompt is detected."
    (when (let ((case-fold-search t))
            (string-match comint-prompt-regexp string))
      (alert (format "Prompt: %s" string)))
    string)

  (defun comint-toggle-alert ()
    "Toggle alert on prompt for current buffer"
    (interactive)
    (make-local-variable 'comint-output-filter-functions)
    (if (member 'comint-alert-on-prompt comint-output-filter-functions)
        (remove-hook 'comint-output-filter-functions 'comint-alert-on-prompt)
      (add-hook 'comint-output-filter-functions #'comint-alert-on-prompt)))
  :config
  (setq alert-default-style
        (if (eq window-system 'ns)
            'notifier
          'notifications)))

amx

(use-package amx
  :bind (("M-X" . amx-major-mode-commands))
  :init (amx-mode))

auth-source-pass

(use-package auth-source-pass
  :init (auth-source-pass-enable))

avy

(use-package avy
  :bind (("C-c a" . avy-goto-char-timer)
         ("M-g M-g" . avy-goto-line)))

bpr

(define-derived-mode bpr-shell-mode
            shell-mode "BPR"
            "Major mode for BPR process buffers.")

(defun my-bpr-on-start (process)
  (set-process-filter process 'comint-output-filter))

;;;###autoload
(defun my-bpr-switch-to-last-buffer ()
  "Opens the buffer of the last spawned process."
  (interactive)
  (if (buffer-live-p bpr-last-buffer)
      (switch-to-buffer bpr-last-buffer)
    (message "Can't find last used buffer")))

(defun my-bpr-spawn (open-buffer)
  "Run 'bpr-spawn'.
If OPEN-BUFFER is set, open the new buffer."
  (interactive "P")
  (call-interactively #'bpr-spawn)
  (if open-buffer
      (my-bpr-switch-to-last-buffer)))

(use-package bpr
  :bind (("M-&" . my-bpr-spawn))
  :config
  (setq bpr-show-progress nil
        bpr-on-start #'my-bpr-on-start
        bpr-process-mode #'bpr-shell-mode
        bpr-use-projectile nil))

cider

(use-package cider
  :config
  (setq cider-prompt-for-symbol nil)
  (setq cider-repl-history-file "~/.emacs.d/cider-history")
  (setq cider-repl-use-pretty-printing t)
  (setq cider-show-error-buffer nil)
  (add-hook 'cider-repl-mode-hook #'smartparens-strict-mode))

clojure-mode

(use-package clojure-mode
  :config
  (add-hook 'clojure-mode-hook #'smartparens-strict-mode))

comint-mode

Add more password prompts.

(setq comint-input-ignoredups t
      comint-input-ring-size 10000
      comint-password-prompt-regexp
      (concat comint-password-prompt-regexp
              "\\|"
              ;; OpenStack
              "Please enter your OpenStack Password:"
              "\\|"
              ;; curl
              "Enter host password for user '[^']*':"
              "\\|"
              ;; Ansible
              "SUDO password:"
              "\\|"
              "Vault password:"
              "\\|"
              ;; openssl pkcs12 -nocerts -nodesopenssl
              "Enter Import Password:"
              "\\|"
              ;; sshuttle
              "[local sudo] Password:"
              "\\|"
              ;; Java keytool
              "Enter keystore password:"))

Change scrolling behavior for comint modes.

(defun comint-mode-config()
  ;; Do not move prompt to bottom of the screen on output
  (setq comint-scroll-show-maximum-output nil)
  ;; Do not center the prompt when scrolling
  ;;
  ;; ("If the value is greater than 100, redisplay will never recenter
  ;; point, but will always scroll just enough text to bring point
  ;; into view, even if you move far away.")
  (setq-local scroll-conservatively 101)
  (setq-local auto-hscroll-mode 'current-line))

(add-hook 'comint-mode-hook #'comint-mode-config)

company

(use-package company
  :diminish company-mode
  :init
  (global-company-mode)
  :config
  (global-set-key (kbd "TAB") #'company-indent-or-complete-common)
  (setq company-show-numbers t
        company-minimum-prefix-length 2))

company-jedi

(use-package company-jedi
  ;; :init (add-hook 'python-mode-hook 'jedi:setup)
  :config
  (setq jedi:use-shortcuts t))

counsel

(use-package counsel
  :bind (("C-c y" . counsel-yank-pop)
         ("C-x C-f" . counsel-find-file))
  :config
  (setq counsel-find-file-at-point t
        counsel-rg-base-command "rg --smart-case --no-heading --line-number --max-columns 150 --color never %s ."))

desktop

(use-package desktop
  :config
  (defun my-shell-save-desktop-data (desktop-dirname)
    "Extra info for shell-mode buffers to be saved in the desktop file."
    (list default-directory comint-input-ring))

  (defun my-shell-restore-desktop-buffer
      (desktop-buffer-file-name desktop-buffer-name desktop-buffer-misc)
    "Restore a shell buffer's state from the desktop file."
    (let ((dir (nth 0 desktop-buffer-misc))
          (ring (nth 1 desktop-buffer-misc)))
      (when desktop-buffer-name
        (set-buffer (get-buffer-create desktop-buffer-name))
        (when dir
          (setq default-directory dir))
        (shell desktop-buffer-name)
        (when ring
          (setq comint-input-ring ring))
        (current-buffer))))

  (defun my-shell-setup-desktop ()
    "Sets up a shell buffer to have its state saved in the desktop file."
    (setq-local desktop-save-buffer #'my-shell-save-desktop-data))

  (add-to-list 'desktop-buffer-mode-handlers
               '(shell-mode . my-shell-restore-desktop-buffer))
  (add-hook 'shell-mode-hook #'my-shell-setup-desktop)

  (setq desktop-buffers-not-to-save "\\*Async Shell Command\\*\\|\\*shell\\*<")

  ;; Do not save GPG-encrypted files to the desktop
  (setq desktop-files-not-to-save "\\(^/[^/:]*:\\|(ftp)$\\|\\.gpg$\\)")
  ;; Do not save BPR shell buffers
  (setq desktop-modes-not-to-save '(tags-table-mode bpr-shell-mode))
  ;; Load 20 buffers on start, then lazily restore emaining buffer
  (setq desktop-restore-eager 20)
  ;; Do not save frame and window configuration (saving these leaves artifacts
  ;; from loaded themes)
  (setq desktop-restore-frames nil)
  ;; Auto-save desktop periodically
  (setq desktop-auto-save-timeout 10)

  :init
  (defun my-desktop-remove-stale-lock (&optional dirname)
    "Remove stale desktop lock file in DIRNAME.
DIRNAME omitted or nil means use `desktop-dirname'."
    ;; (require 'desktop)
    (let ((pid (desktop-owner dirname)))
      (when pid
        (let ((infile nil)
              (destination nil)
              (display nil))
          (unless (= (call-process "ps" infile destination display "-p"
                                   (number-to-string pid)) 0)
            (let ((lock-name (desktop-full-lock-name dirname)))
              (message "removing stale lock: %s, pid: %s" lock-name pid)
              (delete-file lock-name)))))))

  (add-hook 'desktop-save-mode-hook (lambda () (my-desktop-remove-stale-lock "~/.emacs.d")))

  (desktop-save-mode 1))

dired

(use-package dired
  :config
  (setq dired-dwim-target t)
  (defun my-dired-mode-hook ()
    (setq truncate-lines t))
  (add-hook 'dired-mode-hook #'my-dired-mode-hook))

dns-mode

(use-package dns-mode
  :defer t
  :config
  ;; Do not auto increment serial (C-c C-s to increment)
  (setq dns-mode-soa-auto-increment-serial nil))

eglot

(use-package eglot
  :bind
  (("C-c h" . eglot-help-at-point))
  :config
  ;; Use rust-analyzer instead of rls
  (add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer"))
  (add-to-list 'eglot-server-programs '(terraform-mode "terraform-lsp"))
  (defun my-eglot-hook()
    (if (eglot-managed-p)
        (add-hook 'before-save-hook #'eglot-format-buffer)))
  (add-hook 'eglot-managed-mode-hook #'my-eglot-hook)
  :hook
  (go-mode . eglot-ensure)
  (rust-mode . eglot-ensure)
  (terraform-mode . eglot-ensure))

epresent

(use-package epresent
  :defer t
  :config
  (setq epresent-face-attributes '((default :height 300)))
  (defun my-epresent-hook ()
    (setq-local global-hl-line-mode nil))
  (add-hook 'epresent-start-presentation-hook #'my-epresent-hook))

erc

(use-package erc
  :defer t
  :config
  (erc-services-mode 1)
  (erc-spelling-mode 1)

  (setq erc-hide-list '("JOIN" "MODE" "PART" "QUIT"))

  ;; Nickserv configuration
  (setq erc-nick "svend")
  (setq erc-prompt-for-nickserv-password nil)
  (let ((bitlbee-username (password-store-get "bitlbee-username"))
        (bitlbee-password (password-store-get "bitlbee-password"))
        (freenode-username (password-store-get "freenode/username"))
        (freenode-password (password-store-get "freenode/password")))
    (setq erc-nickserv-passwords
          `((BitlBee ((,bitlbee-username . ,bitlbee-password)))
            ((freenode ((,freenode-username . ,freenode-password)))))))

  (setq erc-autojoin-channels-alist '(("freenode.net" "#nixos" "##nix-darwin" "#org-mode" "#emacs"))))

erc-track

(use-package erc-track
  :defer t
  :config
  ;; Do not notify for join, part, or quit
  (add-to-list 'erc-track-exclude-types "JOIN")
  (add-to-list 'erc-track-exclude-types "PART")
  (add-to-list 'erc-track-exclude-types "QUIT"))

expand-region

(use-package expand-region
  :bind (("M-S-SPC" . er/expand-region)))

flycheck

(use-package flycheck
  :init
  (use-package flycheck-ledger
    :defer t)
  (use-package flycheck-rust
    :config
    (add-hook 'flycheck-mode-hook #'flycheck-rust-setup))
  (use-package flycheck-golangci-lint
    :config
    (add-hook 'flycheck-mode-hook #'flycheck-golangci-lint-setup))
  :config
  ;; (add-hook 'flycheck-mode-hook #'flycheck-cask-setup)
  (setq flycheck-python-flake8-executable "python3"
        flycheck-python-pylint-executable "python3")
  (flycheck-add-mode #'yaml-ruby #'ansible-playbook-mode)
  (flycheck-add-next-checker 'chef-foodcritic 'ruby-rubocop)
  (add-hook 'after-init-hook #'global-flycheck-mode))

git

(use-package git
  :defer t
  :config
  (defun my-git-clone (url)
    (interactive "sGit repository URL: ")
    (let ((git-repo "~/src"))
      (git-clone url))))

git-commit

(use-package git-commit)

gnuplot-mode

(use-package gnuplot-mode
  :mode
  (("\\.gnuplot\\'" . gnuplot-mode)
   ("\\.gp\\'" . gnuplot-mode)))

gnus

Sanitized version of .authinfo.gpg for Gmail IMAP and SMTP.

gpg --batch -d ~/.authinfo.gpg | awk '/\.gmail\.com/{$4="EMAIL";$6="PASSWORD";print}'
pass show imap.gmail.com | sed -e '1s/.*/PASSWORD/' -e '/user:/s/[^ ]*$/EMAIL/'
pass show smtp.gmail.com | sed -e '1s/.*/PASSWORD/' -e '/user:/s/[^ ]*$/EMAIL/'
(use-package gnus
  :config
  ;; Use secondary-select-methods
  (setq gnus-select-method '(nnnil ""))

  ;; ;; Gmane
  (add-to-list 'gnus-secondary-select-methods
               '(nntp "news.gmane.org"))

  ;; Fastmail
  (add-to-list 'gnus-secondary-select-methods
               '(nnimap "imap.fastmail.com"))
  ;; Gmail
  (add-to-list 'gnus-secondary-select-methods
               '(nnimap "imap.gmail.com"))

  ;; (add-to-list 'gnus-secondary-select-methods
  ;;              '(nnimap "imap.gmail.com"
  ;;                       (nnimap-address "imap.gmail.com")
  ;;                       ;; (nnimap-server-port 993)
  ;;                       ;; (nnimap-stream ssl)
  ;;                       ))

  ;; ;; Record all IMAP commands in the ‘"*imap log*"’
  ;; (setq nnimap-record-commands t)

  ;; Skip prompt: "Gnus auto-save file exists. Do you want to read it?"
  (setq gnus-always-read-dribble-file t
        ;; Mark sent messages as read
        gnus-gcc-mark-as-read t
        gnus-inhibit-startup-message t
        ;; Do not take over the entire frame
        gnus-use-full-window nil))

gnus-alias

(use-package gnus-alias
  :defer t
  :config
  (setq gnus-alias-identity-alist
        '(("fastmail" nil "Svend Sorensen <[email protected]>" nil (("Bcc" . "[email protected]")) nil)))
  (setq gnus-alias-default-identity "fastmail")
  (setq gnus-alias-identity-rules '()))

gnutls

(use-package gnutls
  :defer t
  :config
  (add-to-list 'gnutls-trustfiles
               (expand-file-name "~/.certs/ca-bundle.crt")))

go-mode

  • godef (for go-mode’s godef-* commands)
  • goimports (for gofmt-command)
  • golint (used by flycheck)
  • golangci-lint (used by flycheck-golangci-lint)
go get -u golang.org/x/tools/cmd/goimports
go get -u github.com/rogpeppe/godef
go get -u github.com/golang/lint/golint
go get -u github.com/stamblerre/gocode
brew install golangci-lint
(use-package go-mode
  :defer t
  :mode
  ;; modes do not exist for go module files
  (("go.mod\\'" . fundamental-mode)
   ("go.sum\\'" . fundamental-mode))
  :config
  (setq gofmt-command "goimports")

  (defun my-go-mode-defaults ()
    ;; Use eglot-format-buffer hook
    ;; (add-hook 'before-save-hook #'gofmt-before-save)
    ;; CamelCase aware editing operations
    (subword-mode +1))
  (add-hook 'go-mode-hook #'my-go-mode-defaults))

groovy-mode

(use-package groovy-mode
  :config
  (defun my-groovy-mode-hook ()
    ;; Indent groovy code four spaces instead of two
    (setq c-basic-offset 4))
  (add-hook 'groovy-mode-hook #'my-groovy-mode-hook)
  :mode
  (("Jenkinsfile\\'" . groovy-mode)))

haskell-mode

(use-package haskell-mode
  :defer t
  :config
  (defun my-haskell-mode-defaults ()
    (subword-mode +1)
    (turn-on-haskell-doc-mode)
    (turn-on-haskell-indentation)
    (interactive-haskell-mode +1))
  (add-hook 'haskell-mode-hook #'my-haskell-mode-defaults))

hippie-exp

(use-package hippie-exp
  :bind (("M-/" . hippie-expand)))

hydra

(use-package hydra
  :defer t
  :config
  (global-set-key
   (kbd "C-c t")
   (defhydra hydra-toggle (:timout 10)
     "Toggle"
     ("b" scroll-bar-mode "scroll-bar")
     ("c" flycheck-mode "flycheck")
     ("f" variable-pitch-mode "fixed-pitch")
     ("h" global-hl-line-mode "hl-line")
     ("l" visual-line-mode "visual-line")
     ("m" menu-bar-mode "menu-bar")
     ("n" my-toggle-line-numbers "line-numbers")
     ("o" overwrite-mode "overwrite")
     ("s" flyspell-mode "flyspell")
     ("t" toggle-truncate-lines "trucate")
     ("v" visual-fill-column-mode "visual-fill-column")
     ("w" whitespace-mode "whitespace")))

  (defhydra hydra-winner ()
    "Winner"
    ("w" winner-undo "back")
    ("r" winner-redo "forward" :exit t))
  (global-set-key (kbd "C-c w") 'hydra-winner/winner-undo))

ibuffer

(use-package ibuffer
  :bind (("C-x C-b" . ibuffer)))

ibuffer-tramp

(use-package ibuffer-tramp
  :config
  (add-hook 'ibuffer-hook
            (lambda ()
              (ibuffer-tramp-set-filter-groups-by-tramp-connection)
              (ibuffer-do-sort-by-alphabetic))))

inf-ruby

(use-package inf-ruby
  :defer t
  :config
  (defun my-inf-ruby-mode-setup ()
    (setq comint-input-ring-file-name "~/.pry_history")
    (when (ring-empty-p comint-input-ring)
      (comint-read-input-ring t)))
  (add-hook 'inf-ruby-mode-hook #'my-inf-ruby-mode-setup)
  (setq inf-ruby-default-implementation "pry"))

ivy

(use-package ivy
  :diminish ivy-mode
  :bind (("C-c s" . swiper))
  :config
  (setq ivy-re-builders-alist '((amx-completing-read-ivy . ivy--regex-fuzzy)
                                (t . ivy--regex-plus))
        ivy-magic-tilde nil
        ivy-use-virtual-buffers t)
  :init
  (ivy-mode 1))

json-mode

Terraform state files are JSON.

(use-package json-mode
  :defer t
  :mode ("\\.tfstate\\'" "\\.tfstate\\.backup\\'")
  :config
  ;; Two-space indentation for JSON (default if 4)
  (setq json-reformat:indent-width 2)
  (add-hook 'json-mode-hook
            (lambda ()
              (setq-local js-indent-level 2))))

kubernetes

(use-package kubernetes
  :defer t
  :commands (kubernetes-use-context))

lisp-mode

(use-package lisp-mode
  :config
  (add-hook 'emacs-lisp-mode-hook
            (lambda ()
              (smartparens-strict-mode))))

lsp-mode

(use-package lsp-mode
  ;; :hook
  ;; (go-mode . lsp-deferred)
  ;; (rust-mode . lsp-deferred)
  :config (setq lsp-auto-guess-root t
                lsp-clients-python-command "pyls-pipenv"
                lsp-restart 'auto-restart)
  :commands lsp)

lsp-ui-mode

(use-package lsp-ui
  :config
  (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
  (define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references)
  :commands lsp-ui-mode)

magit

(use-package magit
  :bind (("C-c m" . magit-dispatch-popup))
  :init
  ;; We have global-auto-revert mode enabled
  (setq magit-auto-revert-mode nil)
  :config
  (setq magit-completing-read-function 'ivy-completing-read
        magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1
        magit-repository-directories '("~/src")
        magit-save-repository-buffers 'dontask)

  (add-hook 'after-save-hook #'magit-after-save-refresh-status))

markdown-mode

(use-package markdown-mode
  :defer t
  :mode (("README\\.md\\'" . gfm-mode)
         ("CHANGELOG\\.md\\'" . markdown-mode)))

message

(use-package message
  :defer t
  :config
  ;; Internal SMTP library
  (setq message-send-mail-function 'smtpmail-send-it
        smtpmail-smtp-server "smtp.fastmail.com"
        smtpmail-smtp-service 587)

  ;; OR

  ;; Use MSMTP with auto-smtp selection
  ;; https://www.emacswiki.org/emacs/GnusMSMTP#toc3
  ;;
  (setq sendmail-program "/usr/bin/msmtp"
        mail-specify-envelope-from t
        mail-envelope-from 'header
        message-sendmail-envelope-from 'header)

  ;; Enable notmuch-address completion
  ;; (notmuch-address-message-insinuate)

  ;; Enable gnus-alias
  (add-hook 'message-setup-hook #'gnus-alias-determine-identity)
  (define-key message-mode-map (kbd "C-c C-p") 'gnus-alias-select-identity))

native-complete

(use-package native-complete
  :config
  ;; Add = to enable completion for --option= flags
  (setq native-complete-exclude-regex "[^$(-/_~=[:alnum:]]")

  (with-eval-after-load 'shell
    (native-complete-setup-bash))

  (defun my-shell-hook ()
    (setq completion-at-point-functions '(native-complete-at-point)))

  (add-hook 'shell-mode-hook #'my-shell-hook))

Notes for testing native-complete.

Good tips here: CeleritasCelery/emacs-native-shell-complete#3 (comment)

Start Emacs

open -a ~/.nix-profile/Applications/Emacs.app --new --args -q
# or
open -a ~/.nix-profile/Applications/Emacs.app --new --args -q --load ~/src/emacs-native-shell-complete/native-complete.el
(package-initialize)
(setq shell-file-name "bash") ;; Its value is "/bin/bash"; Original value was "/bin/sh"
;; Enable completion for --opt=val flags
(setq native-complete-exclude-regex "[^$(-/_~=[:alnum:]]")

(with-eval-after-load 'shell
  (native-complete-setup-bash))

(defun my-shell-hook ()
  (setq completion-at-point-functions '(native-complete-at-point)))

(add-hook 'shell-mode-hook #'my-shell-hook)

nix-mode

(use-package nix-mode
  :config
  (setq nix-nixfmt-bin "nixpkgs-fmt"))

notmuch

(use-package notmuch
  :defer t
  :config
  ;; notmuch-always-prompt-for-sender requires ido-mode
  ;; Add (ido-mode t) to emacs configuration
  (setq notmuch-always-prompt-for-sender t)

  ;; Use Bcc instead of Fcc
  (setq notmuch-fcc-dirs nil)

  ;; Show newest mail first
  (setq notmuch-search-oldest-first nil)

  ;; ;; Notmuch remote setup (on all hosts except garnet)
  ;; (when (not (string= system-name "garnet.ciffer.net"))
  ;;   (setq notmuch-command "notmuch-remote"))

  ;; Getting Things Done (GTD) keybindings

  (setq notmuch-tag-macro-alist
        (list
         '("a" "+action" "-waiting" "-inbox")
         '("w" "-action" "+waiting" "-inbox")
         '("d" "-action" "-waiting" "-inbox")))

  (defun notmuch-search-apply-tag-macro (key)
    (interactive "k")
    (let ((macro (assoc key notmuch-tag-macro-alist)))
      (notmuch-search-tag (cdr macro))))

  (defun notmuch-show-apply-tag-macro (key)
    (interactive "k")
    (let ((macro (assoc key notmuch-tag-macro-alist)))
      (notmuch-show-tag (cdr macro))))

  (define-key notmuch-search-mode-map "`" 'notmuch-search-apply-tag-macro)
  (define-key notmuch-show-mode-map "`" 'notmuch-show-apply-tag-macro))

nov

nov.el (clever name) is an EPUB reader package.

(use-package nov
  :mode ("\\.epub\\'" . nov-mode)
  :config
  (setq nov-save-place-file (expand-file-name "nov-save-place" user-emacs-directory)))

ob-go

package main

import "fmt"

func main() {
	fmt.Println("Hello, world")
}
(use-package ob-go)

ob-rust

Requires cargo-script.

cargo install cargo-script
fn main() {
    for count in 0..3 {
        println!("{}. Hello World!", count);
    }
}
(use-package ob-rust)

org

(use-package org
  :bind (("C-c c" . org-capture)
         ("C-c o a" . org-agenda)
         ("C-c o b" . org-iswitchb)
         ("C-c o l" . org-store-link))
  :config
  (add-hook 'org-mode-hook #'auto-fill-mode)

  (setq org-babel-python-command "python3"
        org-capture-templates  '(("t" "Task" entry (file "tasks.org")
                                  "* TODO %?\n   SCHEDULED: %T\n\n%a" :prepend t))
        org-default-notes-file "~/doc/org/notes.org"
        org-directory "~/doc/org"
        org-ellipsis ""
        org-plantuml-jar-path (expand-file-name "~/.nix-profile/lib/plantuml.jar")
        org-refile-targets '((nil :maxlevel . 9))
        org-reverse-note-order t
        org-src-ask-before-returning-to-edit-buffer nil
        org-src-preserve-indentation t
        org-src-window-setup 'current-window
        org-startup-with-inline-images t
        org-use-speed-commands t)

  (org-babel-do-load-languages
   'org-babel-load-languages
   '((calc . t)
     (emacs-lisp . t)
     (perl . t)
     (plantuml . t)
     (python . t)
     (ruby . t)
     (shell . t))))

org-capture

(use-package org-capture
  :bind (("C-c o c" . org-capture))
  :config
  (setq org-capture-templates
        '(("t" "Task" entry (file "tasks.org")
           "* TODO %?\n   SCHEDULED: %T\n\n%a" :prepend t))))

pdf-tools

(use-package pdf-tools
  :init
  (pdf-tools-install))

plantuml-mode

(use-package plantuml-mode
  :config
  (setq plantuml-jar-path "/usr/local/opt/plantuml/libexec/plantuml.jar"))

project

(use-package project
  :config
  ;; Support go projects that are in a subdirectory of a git repository
  (add-to-list 'project-find-functions #'project-try-go)

  (defun project-try-go (dir)
    (let ((path (locate-deepest-file dir "go.mod")))
      (when path
        (cons 'transient dir))))

  (defun locate-deepest-file (file name)
    (let ((path (locate-dominating-file file name)))
      (when path
        (let ((parent (file-name-directory (directory-file-name path))))
          (or (locate-deepest-file parent name) path))))))

projectile

(use-package projectile
  :init (projectile-mode)
  :config
  (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
  ;; Mark projectile variables as safe
  (seq-doseq (var '(projectile-project-compilation-cmd
                    projectile-project-test-cmd
                    projectile-project-run-cmd))
    (put var 'safe-local-variable #'stringp))

  (setq projectile-completion-system 'ivy)
  (setq projectile-use-git-grep t))

python

(use-package python
  :config
  (setq python-shell-interpreter "pipenv-try"
        python-shell-interpreter-args "python3 -i")

  (defun my-python-mode-defaults ()
    ;; PEP 8 compliant filling rules, 79 chars maximum
    (setq fill-column 79)
    ;; (add-hook 'before-save-hook #'py-isort-before-save)
    )

  (add-hook 'python-mode-hook #'my-python-mode-defaults)

  (defun my-inferior-python-mode-setup ()
    (setq comint-input-ring-file-name "~/.python_history")
    (when (ring-empty-p comint-input-ring)
      (comint-read-input-ring t)))
  (add-hook 'inferior-python-mode-hook #'my-inferior-python-mode-setup))

quickrun

Increase timeout to 60 seconds from the default of 10 seconds.

(use-package quickrun
  :bind (("C-c q a" . quickrun-with-arg)
         ("C-c q q" . quickrun)
         ("C-c q r" . quickrun-region)
         ("C-c q s" . quickrun-shell))
  :config
  (setq quickrun-timeout-seconds 60))

recentf

(use-package recentf
  :init (recentf-mode 1)
  :config
  ;; Increase size of recent file list
  (setq recentf-max-saved-items 1000)

  ;; Ignore temporary notmuch ical files
  (add-to-list 'recentf-exclude "^/tmp/notmuch-ical"))

robe

(use-package robe
  :config
  (add-hook 'ruby-mode-hook #'robe-mode))

ruby-mode

Ruby auto-modes. These are from prelude.

(use-package ruby-mode
  :mode
  (("\\.rake\\'" . ruby-mode)
   ("Rakefile\\'" . ruby-mode)
   ("\\.gemspec\\'" . ruby-mode)
   ("\\.ru\\'" . ruby-mode)
   ("Gemfile\\'" . ruby-mode)
   ("Guardfile\\'" . ruby-mode)
   ("Capfile\\'" . ruby-mode)
   ("\\.thor\\'" . ruby-mode)
   ("\\.rabl\\'" . ruby-mode)
   ("Thorfile\\'" . ruby-mode)
   ("Vagrantfile\\'" . ruby-mode)
   ("\\.jbuilder\\'" . ruby-mode)
   ("Podfile\\'" . ruby-mode)
   ("\\.podspec\\'" . ruby-mode)
   ("Puppetfile\\'" . ruby-mode)
   ("Berksfile\\'" . ruby-mode)
   ("Appraisals\\'" . ruby-mode))
  :config
  (defun my-ruby-mode-defaults ()
    (inf-ruby-minor-mode +1)
    (ruby-tools-mode +1)
    ;; CamelCase aware editing operations
    (subword-mode +1))
  (add-hook 'ruby-mode-hook #'my-ruby-mode-defaults))

rust-mode

(use-package rust-mode
  :defer t
  :config
  ;; Use eglot-format-buffer hook
  ;; (setq rust-format-on-save t)
  (add-to-list 'interpreter-mode-alist '("run-cargo-script" . rust-mode)))

savehist

(use-package savehist
  :init (savehist-mode 1))

saveplace

(use-package saveplace
  :init (save-place-mode))

server

Start Emacs server unless one is already running. server-running-p requires server.

(use-package server
  :config
  (unless (server-running-p)
    (server-start)))

sh-script

(use-package sh-script
  :defer t
  :config
  (defun my-setup-sh-mode ()
    "My preferences for sh-mode"
    (setq sh-basic-offset 2)
    (setq sh-indent-after-continuation 'always)
    (setq sh-indent-for-case-alt '+)
    (setq sh-indent-for-case-label 0))

  (add-hook 'sh-mode-hook #'my-setup-sh-mode))

shell

See https://stackoverflow.com/a/11255996

(defun shell-mode-config ()
  ;; company-mode
  ;;
  ;; Disable idle completion
  (setq-local company-idle-delay nil)
  ;; Tab to complete. Use company-complete-common instead of
  ;; company-manual-begin to complete on tab.
  (define-key shell-mode-map (kbd "TAB") #'company-complete-common)

  ;; Do not store duplicate history entries
  (setq comint-input-ignoredups t))
(use-package shell
  :config
  (add-to-list 'display-buffer-alist
               '("^\\*shell\\*" . ((display-buffer-reuse-window display-buffer-same-window))))
  ;; bash-completion only loaded for login shells; note that "--login" must come
  ;; before short options like "-i"
  (add-to-list 'explicit-bash-args "--login")
  (setq explicit-shell-file-name "bash")
  ;; Do not try to colorize comments and strings in shell mode
  (setq shell-font-lock-keywords nil)
  ;; This seems to be slowing down shell buffers
  ;; (remove-hook 'shell-mode-hook 'goto-address-mode)
  (add-hook 'shell-mode-hook #'shell-mode-config))

To disable scroll to bottom:

(remove-hook 'comint-output-filter-functions
             'comint-postoutput-scroll-to-bottom)

Changing directory generates a message with the new directory path. To disable this:

(setq shell-dirtrack-verbose nil)

To search history when you are at a command line using C-r (instead of M-r):

(setq comint-history-isearch dwim)

slime

Slime Installation

(use-package slime
  :defer t
  :config
  (setq inferior-lisp-program "sbcl"))

smartparens

(use-package smartparens
  :diminish smartparens-mode
  :init
  (smartparens-global-mode t)
  (require 'smartparens-config)
  (sp-use-paredit-bindings)
  ;; sp-paredit-bindings: ("M-r" . sp-splice-sexp-killing-around)
  (define-key sp-keymap (kbd "M-r") nil)
  (define-key sp-keymap (kbd "M-s") nil)
  ;; sp-smartparens-bindings: ("M-<backspace>" . sp-backward-unwrap-sexp)
  (define-key sp-keymap (kbd "M-<backspace>") nil))

super-save

(use-package super-save
  :init (super-save-mode +1)
  :diminish super-save-mode
  :config
  (add-to-list 'super-save-triggers #'ace-window)
  (setq super-save-auto-save-when-idle t))

swiper

(use-package swiper
  :bind (("C-c s" . swiper)))

term

(defun my-setup-term-mode ()
  "My preferences for term mode"
  ;; Settings recommended in term.el
  ;;
  ;; https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/term.el?id=c720ef1329232c76d14a0c39daa00e37279aa818#n179
  (setq-local mouse-yank-at-point t)
  ;; End of recommended settings

  ;; Make term mode more term-like

  (define-key term-raw-map (kbd "<C-backspace>") 'term-send-raw)
  (define-key term-raw-map (kbd "<C-S-backspace>") 'term-send-raw)

  ;; Toogle between line and char mode in term-mode
  (define-key term-raw-map (kbd "C-'") 'term-line-mode)
  (define-key term-mode-map (kbd "C-'") 'term-char-mode)

  ;; Enable Emacs key bindings in term mode
  (define-key term-raw-map (kbd "M-!") nil)
  (define-key term-raw-map (kbd "M-&") nil)
  (define-key term-raw-map (kbd "M-:") nil)
  (define-key term-raw-map (kbd "M-x") nil)

  ;; Paste key bindings for Mac keyboards with no insert
  (define-key term-raw-map (kbd "C-c y") 'term-paste)
  (define-key term-raw-map (kbd "s-v") 'term-paste)

  ;; Enable address links in term mode
  (goto-address-mode))

(use-package term
  :config
  (setq-default term-buffer-maximum-size 10000)
  (add-hook 'term-mode-hook #'my-setup-term-mode))

terraform-mode

(use-package terraform-mode
  :config (add-hook 'terraform-mode-hook #'terraform-format-on-save-mode))

toml-mode

(use-package toml-mode
  :defer t
  :mode
  (("Cargo\\.lock\\'" . toml-mode)))

tramp

Edit remote files via sudo

See https://www.gnu.org/software/emacs/manual/html_node/tramp/Ad_002dhoc-multi_002dhops.html

/ssh:example.com|sudo::/file

Use SSH default control master settings. Add the following to ~/.ssh/config.

ControlMaster auto
ControlPath ~/.ssh/control.%h_%p_%r
ControlPersist 60m
(use-package tramp
  :defer t
  :config
  ;; Frequently Asked Questions: How could I speed up tramp?
  ;; https://www.gnu.org/software/emacs/manual/html_node/tramp/Frequently-Asked-Questions.html
  (setq vc-ignore-dir-regexp
        (format "\\(%s\\)\\|\\(%s\\)"
                vc-ignore-dir-regexp
                tramp-file-name-regexp))

  (setq tramp-use-ssh-controlmaster-options nil
        ;; Tramp sets HISTFILE so bash history on remote shells does not work.
        tramp-histfile-override nil))

Default value of explicit-bash-args is ("--noediting" "-i"). We want login shell for remote hosts. This should be harmless for local shells, however it does increase the start-up time for local shells.

Attempt to start or reattach to a dtach session and fall back to a bash shell.

FIXME: Disabled while switching to native-complete

(setq explicit-bash-args
      '("-c" "dtach -A \"$HOME/.dtach-$(hostname -f 2>/dev/null || hostname)-ssorensen\" -z bash --noediting --login -i 2>/dev/null || bash --noediting --login -i"))
(require 'tramp)

(defun ssh-host-completing-read ()
  (completing-read
   "Open ssh connection to [user@]host: "
   (completion-table-dynamic
    (lambda (str)
      (tramp-completion-handle-file-name-all-completions str "/")))))
(defun ssh-shell (host)
  "Open SSH connection to HOST."
  (interactive (list (ssh-host-completing-read)))
  (let* ((host (if (string-suffix-p ":" host)
                   host
                 (format  "%s:" host)))
         (default-directory (format  "/ssh:%s" host)))
    ;; Opening the shell occasionally hangs and locks up Emacs. Opening a remote
    ;; file first seems to fix this.
    ;;
    ;; Cannot read shell history file when using with-current-buffer.
    (find-file-noselect default-directory)
    (shell (format "*shell*<%s>" host))))

(defun dtach-shell (socket)
  "Attach to specified dtach SOCKET or create it if it does not exist"
  (interactive "F")
  (let ((explicit-shell-file-name "dtach")
        (explicit-dtach-args `("-A" ,socket "-z" "bash" "--noediting" "--login" "-i")))
    (shell (format "*dtach*<%s>" socket))))
(defun tramp-comint-read-input-ring ()
  "Read remote bash_history file into comint input ring."
  (when (tramp-tramp-file-p default-directory)
    (tramp-set-comint-input-ring-file)
    (when (ring-empty-p comint-input-ring)
      (comint-read-input-ring t))))

(defun tramp-set-comint-input-ring-file ()
  "Set the name of the remote comint-input-ring-file."
  (when (tramp-tramp-file-p default-directory)
    (setq comint-input-ring-file-name (format "%s~/.bash_history" default-directory))))

(add-hook 'shell-mode-hook #'tramp-set-comint-input-ring-file)
(add-hook 'shell-mode-hook #'tramp-comint-read-input-ring)

visual-fill-column

(use-package visual-fill-column
  :init
  (dolist (hook '(visual-line-mode-hook
                  cider-repl-mode-hook
                  compilation-mode-hook
                  comint-mode-hook
                  conf-mode-hook
                  custom-mode-hook
                  dired-mode-hook
                  erc-mode-hook
                  eww-mode
                  gnus-article-mode-hook
                  gnus-group-mode-hook
                  gnus-summary-mode-hook
                  Info-mode-hook
                  package-menu-mode-hook
                  prog-mode-hook
                  ;; special-mode-hook ;; FIXME: Text is chopped off in pdf-view mode
                  term-mode-hook
                  text-mode-hook))
    (add-hook hook #'visual-fill-column-mode))
  :config
  (setq-default visual-fill-column-center-text t
                visual-fill-column-fringes-outside-margins nil
                visual-fill-column-width 110)
  (setq split-window-preferred-function #'visual-fill-column-split-window-sensibly))

winner

The winner-mode package provides a way to restore previous window layouts.

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

yaml-mode

(use-package yaml-mode
  :defer t
  :mode (("_helpers\\.tpl\\'" . yaml-mode))           ; Kubernetes Helm
  :config
  (defconst yaml-outline-regex
    (concat "\\( *\\)\\(?:\\(?:--- \\)?\\|{\\|\\(?:[-,] +\\)+\\) *"
            "\\(?:" yaml-tag-re " +\\)?"
            "\\(" yaml-bare-scalar-re "\\) *:"
            "\\(?: +\\|$\\)")
    "Regexp matching a single YAML hash key. This is adds a
    capture group to `yaml-hash-key-re' for the
    indentation.")

  (defun yaml-outline-level ()
    "Return the depth to which a statement is nested in the outline."
    (- (match-end 1) (match-beginning 1)))

  (defun my-yaml-mode-hook()
    (outline-minor-mode)
    (define-key yaml-mode-map (kbd "<backtab>") 'outline-toggle-children)
    (setq-local outline-regexp yaml-outline-regex)
    (setq-local outline-level #'yaml-outline-level))

  (add-hook 'yaml-mode-hook #'my-yaml-mode-hook))

yasnippet

(use-package yasnippet
  :diminish yas-minor-mode
  :init (yas-global-mode))

External config

Load load config stored outside ~/.emacs.d.

(when (file-exists-p "~/.emacs.d/local.el")
  (load-file "~/.emacs.d/local.el"))
(load-file "~/.emacs.d/conf/ob-ansible-playbook.el")

Easy Customization

Save customization in conf/emacs-custom.el instead of init.el.

(setq custom-file "~/.emacs.d/conf/emacs-custom.el")
(load custom-file)

Full screen

(toggle-frame-fullscreen)