(setq user-full-name "Svend Sorensen")
(setq user-mail-address "[email protected]")
(setq package-archives '()) ;; All packages are installed through nixpkgs
(package-initialize)
This is required before any use-package forms.
(eval-when-compile
(require 'use-package))
(require 'diminish)
(require 'bind-key)
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))
Make the cursor a bar instead of a filled box.
(setq-default cursor-type 'bar)
- 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))
- Display column number in mode-line (line number is displayed by default)
(column-number-mode t)
- Enable smooth scrolling.
(pixel-scroll-mode)
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)
I install fonts through nixpkgs.
User fonts can also go in $XDG_DATA_HOME/fonts/
(~/.local/share/fonts
).
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
(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))
Set PAGER to cat to disable less
in async buffers.
(setenv "PAGER" "cat")
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)
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))))
Copy test selected by the mouse to the kill ring. This was turned off in Emacs 24.
(setq mouse-drag-copy-region t)
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)
Enable y/n answers.
(fset 'yes-or-no-p 'y-or-n-p)
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"))
This replaces the selection.
(delete-selection-mode)
(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))
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)
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))
http://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.
http://lists.gnu.org/archive/html/emacs-devel/2011-07/msg01020.html
(setq create-lockfiles nil)
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)))
Enable global auto-revert mode.
(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)
C-c letter
and <F5>
through <F9>
are reserved for user use.
Press C-c C-h
to show all of these.
- Key Binding Conventions
- http://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Binding-Conventions.html
(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)
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)
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"))
(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
;; http://stackoverflow.com/a/3072831/355252
(require 'ansi-color)
(add-hook 'compilation-filter-hook #'my-colorize-compilation-buffer)
(setq initial-major-mode 'fundamental-mode)
(setq initial-scratch-message "Scratch Buffer\n\n")
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")))))
(use-package ace-link
:init (ace-link-setup-default))
(use-package ace-window
:bind (("C-x o" . ace-window)))
(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))
(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)))
(use-package amx
:bind (("M-X" . amx-major-mode-commands))
:init (amx-mode))
(use-package auth-source-pass
:init (auth-source-pass-enable))
(use-package avy
:bind (("C-c a" . avy-goto-char-timer)
("M-g M-g" . avy-goto-line)))
(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))
(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))
(use-package clojure-mode
:config
(add-hook 'clojure-mode-hook #'smartparens-strict-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)
(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))
(use-package company-jedi
;; :init (add-hook 'python-mode-hook 'jedi:setup)
:config
(setq jedi:use-shortcuts t))
(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 ."))
(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))
(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))
(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))
(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))
(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))
(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"))))
(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"))
(use-package expand-region
:bind (("M-S-SPC" . er/expand-region)))
(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))
(use-package git
:defer t
:config
(defun my-git-clone (url)
(interactive "sGit repository URL: ")
(let ((git-repo "~/src"))
(git-clone url))))
(use-package git-commit)
(use-package gnuplot-mode
:mode
(("\\.gnuplot\\'" . gnuplot-mode)
("\\.gp\\'" . gnuplot-mode)))
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))
(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 '()))
(use-package gnutls
:defer t
:config
(add-to-list 'gnutls-trustfiles
(expand-file-name "~/.certs/ca-bundle.crt")))
- 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))
(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)))
(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))
- info:autotype#Hippie Expand
- http://www.gnu.org/software/emacs/manual/html_node/autotype/Hippie-Expand.html
(use-package hippie-exp
:bind (("M-/" . hippie-expand)))
(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))
(use-package ibuffer
:bind (("C-x C-b" . ibuffer)))
(use-package ibuffer-tramp
:config
(add-hook 'ibuffer-hook
(lambda ()
(ibuffer-tramp-set-filter-groups-by-tramp-connection)
(ibuffer-do-sort-by-alphabetic))))
(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"))
(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))
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))))
(use-package kubernetes
:defer t
:commands (kubernetes-use-context))
(use-package lisp-mode
:config
(add-hook 'emacs-lisp-mode-hook
(lambda ()
(smartparens-strict-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)
(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)
(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))
(use-package markdown-mode
:defer t
:mode (("README\\.md\\'" . gfm-mode)
("CHANGELOG\\.md\\'" . markdown-mode)))
(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
;; http://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))
(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)
(use-package nix-mode
:config
(setq nix-nixfmt-bin "nixpkgs-fmt"))
(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.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)))
package main
import "fmt"
func main() {
fmt.Println("Hello, world")
}
(use-package ob-go)
Requires cargo-script.
cargo install cargo-script
fn main() {
for count in 0..3 {
println!("{}. Hello World!", count);
}
}
(use-package ob-rust)
(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))))
(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))))
(use-package pdf-tools
:init
(pdf-tools-install))
(use-package plantuml-mode
:config
(setq plantuml-jar-path "/usr/local/opt/plantuml/libexec/plantuml.jar"))
(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))))))
(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))
(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))
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))
(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"))
(use-package robe
:config
(add-hook 'ruby-mode-hook #'robe-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))
(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)))
(use-package savehist
:init (savehist-mode 1))
(use-package saveplace
:init (save-place-mode))
Start Emacs server unless one is already running. server-running-p
requires server
.
(use-package server
:config
(unless (server-running-p)
(server-start)))
(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))
See http://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)
(use-package slime
:defer t
:config
(setq inferior-lisp-program "sbcl"))
(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))
(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))
(use-package swiper
:bind (("C-c s" . swiper)))
(defun my-setup-term-mode ()
"My preferences for term mode"
;; Settings recommended in term.el
;;
;; http://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))
(use-package terraform-mode
:config (add-hook 'terraform-mode-hook #'terraform-format-on-save-mode))
(use-package toml-mode
:defer t
:mode
(("Cargo\\.lock\\'" . toml-mode)))
Edit remote files via sudo
See http://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)
(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))
The winner-mode package provides a way to restore previous window layouts.
(use-package winner
:init (winner-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))
(use-package yasnippet
:diminish yas-minor-mode
:init (yas-global-mode))
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")
Save customization in conf/emacs-custom.el
instead of init.el
.
(setq custom-file "~/.emacs.d/conf/emacs-custom.el")
(load custom-file)
(toggle-frame-fullscreen)