merge(emacs): Merge (unrelated) emacs.d history into tools/emacs

This commit is contained in:
Vincent Ambo 2019-12-14 11:31:25 +00:00
commit b938d716ff
13 changed files with 1079 additions and 0 deletions

11
tools/emacs/.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
.smex-items
*token*
auto-save-list/
clones/
elpa/
irc.el
local.el
other/
scripts/
themes/
*.elc

6
tools/emacs/README.md Normal file
View file

@ -0,0 +1,6 @@
emacs.d
========
This contains my emacs.d folder.
I use emacs for many things.

168
tools/emacs/init.el Normal file
View file

@ -0,0 +1,168 @@
;;; init.el --- Package bootstrapping. -*- lexical-binding: t; -*-
;; Packages are installed via Nix configuration, this file only
;; initialises the newly loaded packages.
(require 'use-package)
(require 'seq)
(package-initialize)
;; Add 'init' folder that contains other settings to load.
(add-to-list 'load-path (concat user-emacs-directory "init"))
;; Initialise all packages installed via Nix.
;;
;; TODO: Generate this section in Nix for all packages that do not
;; require special configuration.
;;
;; Packages providing generic functionality.
;;
(use-package ace-window
:bind (("C-x o" . ace-window))
:init
(setq aw-keys '(?f ?j ?d ?k ?s ?l ?a)
aw-scope 'frame))
(use-package auth-source-pass :init (auth-source-pass-enable))
(use-package avy
:bind (("M-j" . avy-goto-char)
("M-p" . avy-pop-mark)
("M-g g" . avy-goto-line)))
(use-package browse-kill-ring)
(use-package company
:hook ((prog-mode . company-mode))
:bind (:map rust-mode-map ("<tab>" . company-indent-or-complete-common)
:map lisp-mode-map ("<tab>" . company-indent-or-complete-common))
:init (setq company-tooltip-align-annotations t))
(use-package dash)
(use-package dash-functional)
(use-package edit-server :init (edit-server-start))
(use-package gruber-darker-theme)
(use-package ht)
(use-package hydra)
(use-package idle-highlight-mode :hook ((prog-mode . idle-highlight-mode)))
(use-package paredit :hook ((lisp-mode . paredit-mode)
(emacs-lisp-mode . paredit-mode)))
(use-package multiple-cursors)
(use-package pinentry
:init
(setq epa-pinentry-mode 'loopback)
(pinentry-start))
(use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode))
(use-package rainbow-mode)
(use-package s)
(use-package smartparens :init (smartparens-global-mode))
(use-package string-edit)
(use-package telephone-line) ;; configuration happens outside of use-package
(use-package undo-tree :init (global-undo-tree-mode))
(use-package uuidgen)
(use-package which-key :init (which-key-mode t))
;;
;; Applications in emacs
;;
(use-package magit
:bind ("C-c g" . magit-status)
:init (setq magit-repository-directories '(("/home/vincent/projects" . 2))))
(use-package password-store)
(use-package pg)
(use-package restclient)
;;
;; Packages providing language-specific functionality
;;
(use-package cargo
:hook ((rust-mode . cargo-minor-mode)
(cargo-process-mode . visual-line-mode))
:bind (:map cargo-minor-mode-map ("C-c C-c C-l" . ignore)))
(use-package dockerfile-mode)
(use-package eglot
:init (defvar rust-eglot-initialized nil)
:hook ((rust-mode . (lambda ()
(unless rust-eglot-initialized
(call-interactively #'eglot)
(setq rust-eglot-initialized t))))))
(use-package erlang
:hook ((erlang-mode . (lambda ()
;; Don't indent after '>' while I'm writing
(local-set-key ">" 'self-insert-command)))))
(use-package go-mode)
(use-package haskell-mode)
(use-package jq-mode
:init (add-to-list 'auto-mode-alist '("\\.jq\\'" . jq-mode)))
(use-package kotlin-mode
:bind (:map kotlin-mode-map ("<tab>" . indent-relative)))
(use-package markdown-mode
:init
(add-to-list 'auto-mode-alist '("\\.txt\\'" . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode)))
(use-package markdown-toc)
(use-package nix-mode
:bind (:map nix-mode-map ("<tab>" . nix-indent-line)))
(use-package nginx-mode)
(use-package rust-mode)
(use-package terraform-mode)
(use-package toml-mode)
(use-package web-mode)
(use-package yaml-mode)
;;
;; EXWM / NixOS related packages
;;
;; Configure a few basics before moving on to package-specific initialisation.
(setq custom-file (concat user-emacs-directory "init/custom.el"))
(load custom-file)
(defvar home-dir (expand-file-name "~"))
;; Seed RNG
(random t)
(defun load-other-settings ()
(mapc 'require '(nixos
mail-setup
look-and-feel
functions
settings
modes
bindings
term-setup
eshell-setup))
(telephone-line-setup)
(ace-window-display-mode)
(use-package sly
:init (setq inferior-lisp-program (concat (nix-store-path "sbcl") "/bin/sbcl"))
;;(add-to-list 'company-backends 'sly-company)
))
;; Some packages can only be initialised after the rest of the
;; settings has been applied:
(add-hook 'after-init-hook 'load-other-settings)
(put 'narrow-to-region 'disabled nil)
(put 'upcase-region 'disabled nil)

View file

@ -0,0 +1,54 @@
;; Various keybindings, most of them taken from starter-kit-bindings
;; Font size
(define-key global-map (kbd "C-+") 'text-scale-increase)
(define-key global-map (kbd "C--") 'text-scale-decrease)
;; Use regex searches by default.
(global-set-key (kbd "\C-r") 'isearch-backward-regexp)
(global-set-key (kbd "M-%") 'query-replace-regexp)
(global-set-key (kbd "C-M-s") 'isearch-forward)
(global-set-key (kbd "C-M-r") 'isearch-backward)
(global-set-key (kbd "C-M-%") 'query-replace)
;; Counsel stuff:
(global-set-key (kbd "C-c r g") 'counsel-rg)
;; imenu instead of insert-file
(global-set-key (kbd "C-x i") 'imenu)
;; Window switching. (C-x o goes to the next window)
(windmove-default-keybindings) ;; Shift+direction
;; Start eshell or switch to it if it's active.
(global-set-key (kbd "C-x m") 'eshell)
;; Start a new eshell even if one is active.
(global-set-key (kbd "C-x M") (lambda () (interactive) (eshell t)))
(global-set-key (kbd "C-x p") 'ivy-browse-repositories)
(global-set-key (kbd "M-g M-g") 'goto-line-with-feedback)
(global-set-key (kbd "C-c w") 'whitespace-cleanup)
(global-set-key (kbd "C-c a") 'align-regexp)
;; Browse URLs (very useful for Gitlab's SSH output!)
(global-set-key (kbd "C-c b p") 'browse-url-at-point)
(global-set-key (kbd "C-c b b") 'browse-url)
;; Goodness from @magnars
;; I don't need to kill emacs that easily
;; the mnemonic is C-x REALLY QUIT
(global-set-key (kbd "C-x r q") 'save-buffers-kill-terminal)
(global-set-key (kbd "C-x C-c") 'delete-frame)
;; Open Fefes Blog
(global-set-key (kbd "C-c C-f") 'fefes-blog)
;; Open a file in project:
(global-set-key (kbd "C-c f") 'project-find-file)
;; Use swiper instead of isearch
(global-set-key "\C-s" 'swiper)
(provide 'bindings)

View file

@ -0,0 +1,52 @@
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(ac-auto-show-menu 0.8)
'(ac-delay 0.2)
'(aprila-nixops-path "/home/vincent/projects/langler/nixops")
'(aprila-release-author "Vincent Ambo <vincent@aprila.no>")
'(aprila-releases-path "/home/vincent/projects/langler/docs/releases")
'(avy-background t)
'(cargo-process--custom-path-to-bin "env CARGO_INCREMENTAL=1 cargo")
'(cargo-process--enable-rust-backtrace 1)
'(custom-enabled-themes (quote (gruber-darker)))
'(custom-safe-themes
(quote
("d61fc0e6409f0c2a22e97162d7d151dee9e192a90fa623f8d6a071dbf49229c6" "3c83b3676d796422704082049fc38b6966bcad960f896669dfc21a7a37a748fa" "89336ca71dae5068c165d932418a368a394848c3b8881b2f96807405d8c6b5b6" default)))
'(elnode-send-file-program "/run/current-system/sw/bin/cat")
'(frame-brackground-mode (quote dark))
'(global-auto-complete-mode t)
'(intero-debug nil)
'(intero-global-mode t nil (intero))
'(intero-package-version "0.1.31")
'(kubernetes-commands-display-buffer-function (quote display-buffer))
'(magit-log-show-gpg-status t)
'(ns-alternate-modifier (quote none))
'(ns-command-modifier (quote control))
'(ns-right-command-modifier (quote meta))
'(require-final-newline (quote visit-save)))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(default ((t (:foreground "#e4e4ef" :background "#181818"))))
'(rainbow-delimiters-depth-1-face ((t (:foreground "#2aa198"))))
'(rainbow-delimiters-depth-2-face ((t (:foreground "#b58900"))))
'(rainbow-delimiters-depth-3-face ((t (:foreground "#268bd2"))))
'(rainbow-delimiters-depth-4-face ((t (:foreground "#dc322f"))))
'(rainbow-delimiters-depth-5-face ((t (:foreground "#859900"))))
'(rainbow-delimiters-depth-6-face ((t (:foreground "#268bd2"))))
'(rainbow-delimiters-depth-7-face ((t (:foreground "#cb4b16"))))
'(rainbow-delimiters-depth-8-face ((t (:foreground "#d33682"))))
'(rainbow-delimiters-depth-9-face ((t (:foreground "#839496"))))
'(term-color-black ((t (:background "#282828" :foreground "#282828"))))
'(term-color-blue ((t (:background "#96a6c8" :foreground "#96a6c8"))))
'(term-color-cyan ((t (:background "#1fad83" :foreground "#1fad83"))))
'(term-color-green ((t (:background "#73c936" :foreground "#73c936"))))
'(term-color-magenta ((t (:background "#9e95c7" :foreground "#9e95c7"))))
'(term-color-red ((t (:background "#f43841" :foreground "#f43841"))))
'(term-color-white ((t (:background "#f5f5f5" :foreground "#f5f5f5"))))
'(term-color-yellow ((t (:background "#ffdd33" :foreground "#ffdd33")))))

View file

@ -0,0 +1,68 @@
;; EShell configuration
(require 'eshell)
;; Generic settings
;; Hide banner message ...
(setq eshell-banner-message "")
;; Prompt configuration
(defun clean-pwd (path)
"Turns a path of the form /foo/bar/baz into /f/b/baz
(inspired by fish shell)"
(let* ((hpath (replace-regexp-in-string home-dir
"~"
path))
(current-dir (split-string hpath "/"))
(cdir (last current-dir))
(head (butlast current-dir)))
(concat (mapconcat (lambda (s)
(if (string= "" s) nil
(substring s 0 1)))
head
"/")
(if head "/" nil)
(car cdir))))
(defun vcprompt (&optional args)
"Call the external vcprompt command with optional arguments.
VCPrompt"
(replace-regexp-in-string
"\n" ""
(shell-command-to-string (concat "vcprompt" args))))
(defmacro with-face (str &rest properties)
`(propertize ,str 'face (list ,@properties)))
(defun prompt-f ()
"EShell prompt displaying VC info and such"
(concat
(with-face (concat (clean-pwd (eshell/pwd)) " ") :foreground "#96a6c8")
(if (= 0 (user-uid))
(with-face "#" :foreground "#f43841")
(with-face "$" :foreground "#73c936"))
(with-face " " :foreground "#95a99f")))
(setq eshell-prompt-function 'prompt-f)
(setq eshell-highlight-prompt nil)
(setq eshell-prompt-regexp "^.+? \\((\\(git\\|svn\\|hg\\|darcs\\|cvs\\|bzr\\):.+?) \\)?[$#] ")
;; Ignore version control folders in autocompletion
(setq eshell-cmpl-cycle-completions nil
eshell-save-history-on-exit t
eshell-cmpl-dir-ignore "\\`\\(\\.\\.?\\|CVS\\|\\.svn\\|\\.git\\)/\\'")
;; Load some EShell extensions
(eval-after-load 'esh-opt
'(progn
(require 'em-term)
(require 'em-cmpl)
;; More visual commands!
(add-to-list 'eshell-visual-commands "ssh")
(add-to-list 'eshell-visual-commands "tail")
(add-to-list 'eshell-visual-commands "sl")))
(setq eshell-directory-name "~/.config/eshell/")
(provide 'eshell-setup)

View file

@ -0,0 +1,266 @@
(require 's)
;; A few handy functions I use in init.el (or not, but they're nice to
;; have)
(defun custom-download-theme (url filename)
"Downloads a theme through HTTP and places it in ~/.emacs.d/themes"
;; Ensure the directory exists
(unless (file-exists-p "~/.emacs.d/themes")
(make-directory "~/.emacs.d/themes"))
;; Adds the themes folder to the theme load path (if not already
;; there)
(unless (member "~/.emacs.d/themes" custom-theme-load-path)
(add-to-list 'custom-theme-load-path "~/.emacs.d/themes"))
;; Download file if it doesn't exist.
(let ((file
(concat "~/.emacs.d/themes/" filename)))
(unless (file-exists-p file)
(url-copy-file url file))))
(defun custom-download-script (url filename)
"Downloads an Elisp script, places it in ~/.emacs/other and then loads it"
;; Ensure the directory exists
(unless (file-exists-p "~/.emacs.d/other")
(make-directory "~/.emacs.d/other"))
;; Download file if it doesn't exist.
(let ((file
(concat "~/.emacs.d/other/" filename)))
(unless (file-exists-p file)
(url-copy-file url file))
(load file)))
(defun keychain-password (account &optional keychain)
"Returns the password for the account, by default it's looked up in the Login.keychain but a
different keychain can be specified."
(let ((k (if keychain keychain "Login.keychain")))
(replace-regexp-in-string
"\n" ""
(shell-command-to-string (concat "security find-generic-password -w -a "
account
" "
k)))))
;; This clones a git repository to 'foldername in .emacs.d
;; if there isn't already a folder with that name
(defun custom-clone-git (url foldername)
"Clones a git repository to .emacs.d/foldername"
(let ((fullpath (concat "~/.emacs.d/" foldername)))
(unless (file-exists-p fullpath)
(async-shell-command (concat "git clone " url " " fullpath)))))
(defun load-file-if-exists (filename)
(if (file-exists-p filename)
(load filename)))
(defun goto-line-with-feedback ()
"Show line numbers temporarily, while prompting for the line number input"
(interactive)
(unwind-protect
(progn
(setq-local display-line-numbers t)
(let ((target (read-number "Goto line: ")))
(avy-push-mark)
(goto-line target)))
(setq-local display-line-numbers nil)))
(defun untabify-buffer ()
(interactive)
(untabify (point-min) (point-max)))
(defun indent-buffer ()
(interactive)
(indent-region (point-min) (point-max)))
(defun cleanup-buffer ()
"Perform a bunch of operations on the whitespace content of a buffer.
Including indent-buffer, which should not be called automatically on save."
(interactive)
(untabify-buffer)
(delete-trailing-whitespace)
(indent-buffer))
;; These come from the emacs starter kit
(defun esk-add-watchwords ()
(font-lock-add-keywords
nil '(("\\<\\(FIX\\(ME\\)?\\|TODO\\|DEBUG\\|HACK\\|REFACTOR\\|NOCOMMIT\\)"
1 font-lock-warning-face t))))
(defun esk-sudo-edit (&optional arg)
(interactive "p")
(if (or arg (not buffer-file-name))
(find-file (concat "/sudo:root@localhost:" (read-file-name "File: ")))
(find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
;; Open Fefes blog
(defun fefes-blog ()
(interactive)
(eww "https://blog.fefe.de/"))
;; Open this machines NixOS config
(defun nix-config ()
(interactive)
(find-file "/etc/nixos/configuration.nix"))
;; Open the NixOS man page
(defun nixos-man ()
(interactive)
(man "configuration.nix"))
;; Open local emacs configuration
(defun emacs-config ()
(interactive)
(dired "~/.emacs.d/"))
;; Get the nix store path for a given derivation.
;; If the derivation has not been built before, this will trigger a build.
(defun nix-store-path (derivation)
(let ((expr (concat "with import <nixos> {}; " derivation)))
(s-chomp (shell-command-to-string (concat "nix-build -E '" expr "'")))))
(defun insert-nix-store-path ()
(interactive)
(let ((derivation (read-string "Derivation name (in <nixos>): ")))
(insert (nix-store-path derivation))))
(defun toggle-force-newline ()
"Buffer-local toggle for enforcing final newline on save."
(interactive)
(setq-local require-final-newline (not require-final-newline))
(message "require-final-newline in buffer %s is now %s"
(buffer-name)
require-final-newline))
;; Helm includes a command to run external applications, which does
;; not seem to exist in ivy. This implementation uses some of the
;; logic from Helm to provide similar functionality using ivy.
(defun list-external-commands ()
"Creates a list of all external commands available on $PATH
while filtering NixOS wrappers."
(cl-loop
for dir in (split-string (getenv "PATH") path-separator)
when (and (file-exists-p dir) (file-accessible-directory-p dir))
for lsdir = (cl-loop for i in (directory-files dir t)
for bn = (file-name-nondirectory i)
when (and (not (s-contains? "-wrapped" i))
(not (member bn completions))
(not (file-directory-p i))
(file-executable-p i))
collect bn)
append lsdir into completions
finally return (sort completions 'string-lessp)))
(defun run-external-command (cmd)
"Execute the specified command and notify the user when it
finishes."
(message "Starting %s..." cmd)
(set-process-sentinel
(start-process-shell-command cmd nil cmd)
(lambda (process event)
(when (string= event "finished\n")
(message "%s process finished." process)))))
(defun ivy-run-external-command ()
"Prompts the user with a list of all installed applications and
lets them select one to launch."
(interactive)
(let ((external-commands-list (list-external-commands)))
(ivy-read "Command:" external-commands-list
:require-match t
:history 'external-commands-history
:action #'run-external-command)))
(defun ivy-password-store (&optional password-store-dir)
"Custom version of password-store integration with ivy that
actually uses the GPG agent correctly."
(interactive)
(ivy-read "Copy password of entry: "
(password-store-list (or password-store-dir (password-store-dir)))
:require-match t
:keymap ivy-pass-map
:action (lambda (entry)
(let ((password (auth-source-pass-get 'secret entry)))
(password-store-clear)
(kill-new password)
(setq password-store-kill-ring-pointer kill-ring-yank-pointer)
(message "Copied %s to the kill ring. Will clear in %s seconds."
entry (password-store-timeout))
(setq password-store-timeout-timer
(run-at-time (password-store-timeout)
nil 'password-store-clear))))))
(defun ivy-browse-repositories ()
"Select a git repository and open its associated magit buffer."
(interactive)
(ivy-read "Repository: "
(magit-list-repos)
:require-match t
:sort t
:action #'magit-status))
(defun warmup-gpg-agent (arg &optional exit)
"Function used to warm up the GPG agent before use. This is
useful in cases where there is no easy way to make pinentry run
in the correct context (such as when sending email)."
(interactive)
(message "Warming up GPG agent")
(epg-sign-string (epg-make-context) "dummy")
nil)
(defun bottom-right-window-p ()
"Determines whether the last (i.e. bottom-right) window of the
active frame is showing the buffer in which this function is
executed."
(let* ((frame (selected-frame))
(right-windows (window-at-side-list frame 'right))
(bottom-windows (window-at-side-list frame 'bottom))
(last-window (car (seq-intersection right-windows bottom-windows))))
(eq (current-buffer) (window-buffer last-window))))
(defun inferior-erlang-nix-shell ()
"Start an inferior Erlang process from the root of the current
project."
(interactive)
(inferior-erlang
(format "nix-shell --command erl %s" (cdr (project-current)))))
(defun intero-fix-ghci-panic ()
"Disable deferring of out of scope variable errors, which
triggers a bug in the interactive Emacs REPL printing a panic
under certain conditions."
(interactive)
(let* ((root (intero-project-root))
(package-name (intero-package-name))
(backend-buffer (intero-buffer 'backend))
(name (format "*intero:%s:%s:repl*"
(file-name-nondirectory root)
package-name))
(setting ":set -fno-defer-out-of-scope-variables\n"))
(when (get-buffer name)
(with-current-buffer (get-buffer name)
(goto-char (point-max))
(let ((process (get-buffer-process (current-buffer))))
(when process (process-send-string process setting)))))))
;; Brute-force fix: Ensure the setting is injected every time the REPL
;; is selected.
;;
;; Upstream issue: https://github.com/commercialhaskell/intero/issues/569
(advice-add 'intero-repl :after (lambda (&rest r) (intero-fix-ghci-panic))
'((name . intero-panic-fix)))
(advice-add 'intero-repl-load :after (lambda (&rest r) (intero-fix-ghci-panic))
'((name . intero-panic-fix)))
(provide 'functions)

View file

@ -0,0 +1,115 @@
;;; -*- lexical-binding: t; -*-
;; Hide those ugly tool bars:
(tool-bar-mode 0)
(scroll-bar-mode 0)
(menu-bar-mode 0)
(add-hook 'after-make-frame-functions
(lambda (frame) (scroll-bar-mode 0)))
;; Don't do any annoying things:
(setq ring-bell-function 'ignore)
(setq initial-scratch-message "")
;; Remember layout changes
(winner-mode 1)
;; Usually emacs will run as a proper GUI application, in which case a few
;; extra settings are nice-to-have:
(when window-system
(setq frame-title-format '(buffer-file-name "%f" ("%b")))
(mouse-wheel-mode t)
(blink-cursor-mode -1))
;; Configure editor fonts
(let ((font (format "Input Mono-%d" 12)))
(setq default-frame-alist `((font-backend . "xft")
(font . ,font)))
(set-frame-font font t t))
;; Display battery in mode-line's misc section on adho:
(when (equal "adho" (system-name))
(setq battery-mode-line-format " %b%p%%")
(display-battery-mode))
;; Configure telephone-line
(defun telephone-misc-if-last-window ()
"Renders the mode-line-misc-info string for display in the
mode-line if the currently active window is the last one in the
frame.
The idea is to not display information like the current time,
load, battery levels in all buffers."
(when (bottom-right-window-p)
(telephone-line-raw mode-line-misc-info t)))
(defun telephone-line-setup ()
(telephone-line-defsegment telephone-line-last-window-segment ()
(telephone-misc-if-last-window))
;; Display the current EXWM workspace index in the mode-line
(telephone-line-defsegment telephone-line-exwm-workspace-index ()
(when (bottom-right-window-p)
(format "[%s]" exwm-workspace-current-index)))
;; Define a highlight font for ~ important ~ information in the last
;; window.
(defface special-highlight '((t (:foreground "white" :background "#5f627f"))) "")
(add-to-list 'telephone-line-faces
'(highlight . (special-highlight . special-highlight)))
(setq telephone-line-lhs
'((nil . (telephone-line-position-segment))
(accent . (telephone-line-buffer-segment))))
(setq telephone-line-rhs
'((accent . (telephone-line-major-mode-segment))
(nil . (telephone-line-last-window-segment
telephone-line-exwm-workspace-index))
(highlight . (telephone-line-notmuch-counts))))
(setq telephone-line-primary-left-separator 'telephone-line-tan-left
telephone-line-primary-right-separator 'telephone-line-tan-right
telephone-line-secondary-left-separator 'telephone-line-tan-hollow-left
telephone-line-secondary-right-separator 'telephone-line-tan-hollow-right)
(telephone-line-mode 1))
;; Auto refresh buffers
(global-auto-revert-mode 1)
;; Use clipboard properly
(setq select-enable-clipboard t)
;; Show in-progress chords in minibuffer
(setq echo-keystrokes 0.1)
;; Show column numbers in all buffers
(column-number-mode t)
;; Highlight currently active line
(global-hl-line-mode t)
(defalias 'yes-or-no-p 'y-or-n-p)
(defalias 'auto-tail-revert-mode 'tail-mode)
;; Style line numbers (shown with M-g g)
(setq linum-format
(lambda (line)
(propertize
(format (concat " %"
(number-to-string
(length (number-to-string
(line-number-at-pos (point-max)))))
"d ")
line)
'face 'linum)))
;; Display tabs as 2 spaces
(setq tab-width 2)
;; Don't wrap around when moving between buffers
(setq windmove-wrap-around nil)
(provide 'look-and-feel)

View file

@ -0,0 +1,98 @@
(require 'notmuch)
(require 'counsel-notmuch)
(global-set-key (kbd "C-c m") 'notmuch-hello)
(global-set-key (kbd "C-c C-m") 'counsel-notmuch)
(global-set-key (kbd "C-c C-e n") 'notmuch-mua-new-mail)
(setq notmuch-cache-dir (format "%s/.cache/notmuch" (getenv "HOME")))
(make-directory notmuch-cache-dir t)
;; Cache addresses for completion:
(setq notmuch-address-save-filename (concat notmuch-cache-dir "/addresses"))
;; Don't spam my home folder with drafts:
(setq notmuch-draft-folder "drafts") ;; relative to notmuch database
;; Mark things as read when archiving them:
(setq notmuch-archive-tags '("-inbox" "-unread" "+archive"))
;; Show me saved searches that I care about:
(setq notmuch-saved-searches
'((:name "inbox" :query "tag:inbox" :count-query "tag:inbox AND tag:unread" :key "i")
(:name "aprila-dev" :query "tag:aprila-dev" :count-query "tag:aprila-dev AND tag:unread" :key "d")
(:name "gitlab" :query "tag:gitlab" :key "g")
(:name "sent" :query "tag:sent" :key "t")
(:name "drafts" :query "tag:draft")))
(setq notmuch-show-empty-saved-searches t)
;; Mail sending configuration
(setq send-mail-function 'sendmail-send-it) ;; sendmail provided by MSMTP
(setq notmuch-always-prompt-for-sender t)
(setq notmuch-mua-user-agent-function
(lambda () (format "Emacs %s; notmuch.el %s" emacs-version notmuch-emacs-version)))
(setq mail-host-address (system-name))
(setq notmuch-mua-cite-function #'message-cite-original-without-signature)
;; Close mail buffers after sending mail
(setq message-kill-buffer-on-exit t)
;; Ensure sender is correctly passed to msmtp
(setq mail-specify-envelope-from t
message-sendmail-envelope-from 'header
mail-envelope-from 'header)
;; Store sent mail in the correct folder per account
(setq notmuch-maildir-use-notmuch-insert nil)
(setq notmuch-fcc-dirs '(("mail@tazj.in" . "tazjin/Sent")
;; Not a mistake, Office365 apparently
;; renames IMAP folders (!) to your local
;; language instead of providing translations
;; in the UI m(
("vincent@aprila.no" . "aprila/Sende element")))
;; I don't use drafts but I instinctively hit C-x C-s constantly, lets
;; handle that gracefully.
(define-key notmuch-message-mode-map (kbd "C-x C-s") #'ignore)
;; MSMTP decrypts passwords using pass, but pinentry doesn't work
;; correctly in that setup. This forces a warmup of the GPG agent
;; before sending the message.
;;
;; Note that the sending function is advised because the provided hook
;; for this seems to run at the wrong time.
(advice-add 'notmuch-mua-send-common :before 'warmup-gpg-agent)
;; Define a telephone-line segment for displaying the count of unread,
;; important mails in the last window's mode-line:
(defvar *last-notmuch-count-redraw* 0)
(defvar *current-notmuch-count* nil)
(defun update-display-notmuch-counts ()
"Update and render the current state of the notmuch unread
count for display in the mode-line.
The offlineimap-timer runs every 2 minutes, so it does not make
sense to refresh this much more often than that."
(when (> (- (float-time) *last-notmuch-count-redraw*) 30)
(setq *last-notmuch-count-redraw* (float-time))
(let* ((inbox-unread (notmuch-saved-search-count "tag:inbox and tag:unread"))
(devel-unread (notmuch-saved-search-count "tag:aprila-dev and tag:unread"))
(notmuch-count (format "I: %s; D: %s" inbox-unread devel-unread)))
(setq *current-notmuch-count* notmuch-count)))
(when (and (bottom-right-window-p)
;; Only render if the initial update is done and there
;; are unread mails:
*current-notmuch-count*
(not (equal *current-notmuch-count* "I: 0; D: 0")))
*current-notmuch-count*))
(telephone-line-defsegment telephone-line-notmuch-counts ()
"This segment displays the count of unread notmuch messages in
the last window's mode-line (if unread messages are present)."
(update-display-notmuch-counts))
(provide 'mail-setup)

36
tools/emacs/init/modes.el Normal file
View file

@ -0,0 +1,36 @@
;; Initializes modes I use.
(add-hook 'prog-mode-hook 'esk-add-watchwords)
;; Use auto-complete as completion at point
(defun set-auto-complete-as-completion-at-point-function ()
(setq completion-at-point-functions '(auto-complete)))
(add-hook 'auto-complete-mode-hook
'set-auto-complete-as-completion-at-point-function)
;; Enable rainbow-delimiters for all things programming
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
;; Enable Paredit & Company in Emacs Lisp mode
(add-hook 'emacs-lisp-mode-hook 'company-mode)
;; Always highlight matching brackets
(show-paren-mode 1)
;; Always auto-close parantheses and other pairs
;; (replaced by smartparens)
;; (electric-pair-mode)
;; Keep track of recent files
(recentf-mode)
;; Easily navigate sillycased words
(global-subword-mode 1)
;; Transparently open compressed files
(auto-compression-mode t)
;; Show available key chord completions
(provide 'modes)

103
tools/emacs/init/nixos.el Normal file
View file

@ -0,0 +1,103 @@
;; Configure additional settings if this is one of my NixOS machines
;; (i.e. if ExWM is required)
;; -*- lexical-binding: t; -*-
(require 's)
(require 'f)
(require 'dash)
(defun pulseaudio-ctl (cmd)
(shell-command (concat "pulseaudio-ctl " cmd))
(message "Volume command: %s" cmd))
(defun volume-mute () (interactive) (pulseaudio-ctl "mute"))
(defun volume-up () (interactive) (pulseaudio-ctl "up"))
(defun volume-down () (interactive) (pulseaudio-ctl "down"))
(defun brightness-up ()
(interactive)
(shell-command "exec light -A 10")
(message "Brightness increased"))
(defun brightness-down ()
(interactive)
(shell-command "exec light -U 10")
(message "Brightness decreased"))
(defun lock-screen ()
(interactive)
(shell-command "screen-lock"))
(defun generate-randr-config ()
(-flatten `(,(-map (lambda (n) (list n "DP2")) (number-sequence 1 7))
(0 "eDP1")
,(-map (lambda (n) (list n "eDP1")) (number-sequence 8 9)))))
(use-package exwm
:hook ((exwm-update-class . (lambda ()
;; Make class name the buffer name
(exwm-workspace-rename-buffer exwm-class-name))))
:init
(progn
(require 'exwm-config)
(fringe-mode 3)
(setq exwm-workspace-number 10)
;; 's-r': Reset
(exwm-input-set-key (kbd "s-r") #'exwm-reset)
;; 's-w': Switch workspace
(exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch)
;; 's-N': Switch to certain workspace
(dotimes (i 10)
(exwm-input-set-key (kbd (format "s-%d" i))
`(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
;; Launch applications with completion (dmenu style!)
(exwm-input-set-key (kbd "s-d") #'ivy-run-external-command)
(exwm-input-set-key (kbd "s-p") #'ivy-password-store)
(exwm-input-set-key (kbd "C-s-p") '(lambda ()
(interactive)
(ivy-password-store "~/.aprila-secrets")))
;; Add Alacritty selector to a key
(exwm-input-set-key (kbd "C-x t") #'counsel-switch-to-alacritty)
;; Toggle between line-mode / char-mode
(exwm-input-set-key (kbd "C-c C-t C-t") #'exwm-input-toggle-keyboard)
;; Volume keys
(exwm-input-set-key (kbd "<XF86AudioMute>") #'volume-mute)
(exwm-input-set-key (kbd "<XF86AudioRaiseVolume>") #'volume-up)
(exwm-input-set-key (kbd "<XF86AudioLowerVolume>") #'volume-down)
;; Brightness keys
(exwm-input-set-key (kbd "<XF86MonBrightnessDown>") #'brightness-down)
(exwm-input-set-key (kbd "<XF86MonBrightnessUp>") #'brightness-up)
(exwm-input-set-key (kbd "<XF86Display>") #'lock-screen)
;; Line-editing shortcuts
(exwm-input-set-simulation-keys
'(([?\C-d] . delete)
([?\C-w] . ?\C-c)))
;; Enable EXWM
(exwm-enable)
;; Show time in the mode line
(display-time-mode)
;; Configure xrandr when running on laptop
(when (equal (shell-command-to-string "hostname") "adho\n")
(require 'exwm-randr)
(setq exwm-randr-workspace-output-plist (generate-randr-config))
(exwm-randr-enable))
;; Let buffers move seamlessly between workspaces
(setq exwm-workspace-show-all-buffers t)
(setq exwm-layout-show-all-buffers t)))
(provide 'nixos)

View file

@ -0,0 +1,65 @@
(require 'prescient)
(require 'ivy-prescient)
(require 'uniquify)
(require 'ivy-pass)
;; Make ivy go!
(ivy-mode 1)
(counsel-mode 1)
(setq ivy-use-virtual-buffers t)
(setq enable-recursive-minibuffers t)
;; Enable support for prescient in ivy & configure it
(ivy-prescient-mode)
(prescient-persist-mode)
(add-to-list 'ivy-prescient-excluded-commands 'counsel-rg)
;; Move files to trash when deleting
(setq delete-by-moving-to-trash t)
;; We don't live in the 80s, but we're also not a shitty web app.
(setq gc-cons-threshold 20000000)
(setq uniquify-buffer-name-style 'forward)
; Fix some defaults
(setq visible-bell nil
inhibit-startup-message t
color-theme-is-global t
sentence-end-double-space nil
shift-select-mode nil
uniquify-buffer-name-style 'forward
whitespace-style '(face trailing lines-tail tabs)
whitespace-line-column 80
default-directory "~"
fill-column 80
ediff-split-window-function 'split-window-horizontally)
(add-to-list 'safe-local-variable-values '(lexical-binding . t))
(add-to-list 'safe-local-variable-values '(whitespace-line-column . 80))
(set-default 'indent-tabs-mode nil)
;; UTF-8 please
(setq locale-coding-system 'utf-8) ; pretty
(set-terminal-coding-system 'utf-8) ; pretty
(set-keyboard-coding-system 'utf-8) ; pretty
(set-selection-coding-system 'utf-8) ; please
(prefer-coding-system 'utf-8) ; with sugar on top
;; Make emacs behave sanely (overwrite selected text)
(delete-selection-mode 1)
;; Keep your temporary files in tmp, emacs!
(setq auto-save-file-name-transforms
`((".*" ,temporary-file-directory t)))
(setq backup-directory-alist
`((".*" . ,temporary-file-directory)))
(remove-hook 'kill-buffer-query-functions 'server-kill-buffer-query-function)
;; Show time in 24h format
(setq display-time-24hr-format t)
(provide 'settings)

View file

@ -0,0 +1,37 @@
;; Utilities for Alacritty buffers.
(defun open-or-create-alacritty-buffer (buffer-name)
"Switch to the buffer with BUFFER-NAME or create a
new buffer running Alacritty."
(let ((buffer (get-buffer buffer-name)))
(if (not buffer)
(run-external-command "alacritty")
(switch-to-buffer buffer))))
(defun is-alacritty-buffer (buffer)
"Determine whether BUFFER runs Alacritty."
(and (equal 'exwm-mode (buffer-local-value 'major-mode buffer))
(s-starts-with? "Alacritty" (buffer-name buffer))))
(defun counsel-switch-to-alacritty ()
"Switch to a (multi-)term buffer or create one."
(interactive)
(let ((terms (-map #'buffer-name
(-filter #'is-alacritty-buffer (buffer-list)))))
(if terms
(ivy-read "Switch to Alacritty buffer: "
(cons "New terminal" terms)
:caller 'counsel-switch-to-alacritty
:require-match t
:action #'open-or-create-alacritty-buffer)
(run-external-command "alacritty"))))
(defun alacritty-rename ()
"Rename the current terminal buffer."
(interactive)
(let* ((buffer (get-buffer (buffer-name))))
(if (is-alacritty-buffer buffer)
(rename-buffer (format "Alacritty<%s>" (read-string "New terminal name: ")))
(error "This function is only intended to rename Alacritty buffers."))))
(provide 'term-setup)