chore(emacs.d): Move init/* to config/*

This commit is contained in:
Vincent Ambo 2019-12-14 15:24:53 +00:00
parent 1584607fb9
commit 7d03ab7140
11 changed files with 16 additions and 22 deletions

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)
'(avy-background t)
'(cargo-process--custom-path-to-bin "env CARGO_INCREMENTAL=1 cargo")
'(cargo-process--enable-rust-backtrace 1)
'(company-auto-complete (quote (quote company-explicit-action-p)))
'(company-idle-delay 0.5)
'(custom-enabled-themes (quote (gruber-darker)))
'(custom-safe-themes
(quote
("d61fc0e6409f0c2a22e97162d7d151dee9e192a90fa623f8d6a071dbf49229c6" "3c83b3676d796422704082049fc38b6966bcad960f896669dfc21a7a37a748fa" "89336ca71dae5068c165d932418a368a394848c3b8881b2f96807405d8c6b5b6" default)))
'(display-time-default-load-average nil)
'(display-time-interval 30)
'(elnode-send-file-program "/run/current-system/sw/bin/cat")
'(frame-brackground-mode (quote dark))
'(global-auto-complete-mode t)
'(kubernetes-commands-display-buffer-function (quote display-buffer))
'(lsp-gopls-server-path "/home/tazjin/go/bin/gopls")
'(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))
'(tls-program (quote ("gnutls-cli --x509cafile %t -p %p %h"))))
(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,211 @@
;; -*- lexical-binding: t; -*-
;;
;; Configure desktop environment settings, including both
;; window-management (EXWM) as well as additional system-wide
;; commands.
(require 's)
(require 'f)
(require 'dash)
(require 'exwm)
(require 'exwm-config)
(require 'exwm-randr)
(require 'exwm-systemtray)
(defun pactl (cmd)
(shell-command (concat "pactl " cmd))
(message "Volume command: %s" cmd))
(defun volume-mute () (interactive) (pactl "set-sink-mute @DEFAULT_SINK@ toggle"))
(defun volume-up () (interactive) (pactl "set-sink-volume @DEFAULT_SINK@ +5%"))
(defun volume-down () (interactive) (pactl "set-sink-volume @DEFAULT_SINK@ -5%"))
(defun brightness-up ()
(interactive)
(shell-command "xbacklight -inc 5")
(message "Brightness increased"))
(defun brightness-down ()
(interactive)
(shell-command "xbacklight -dec 5")
(message "Brightness decreased"))
(defun lock-screen ()
(interactive)
;; A sudoers configuration is in place that lets me execute this
;; particular command without having to enter a password.
;;
;; The reason for things being set up this way is that I want
;; xsecurelock.service to be started as a system-wide service that
;; is tied to suspend.target.
(shell-command "/usr/bin/sudo /usr/bin/systemctl start xsecurelock.service"))
(defun generate-randr-config (primary secondary)
(-flatten `(,(-map (lambda (n) (list n primary)) (number-sequence 1 7))
(0 secondary)
,(-map (lambda (n) (list n secondary)) (number-sequence 8 9)))))
(defun randr-layout-dp1-extend ()
"Layout for connecting my X1 Carbon to my screen at home"
(interactive)
(setq exwm-randr-workspace-monitor-plist (generate-randr-config "DP1-1" "eDP1"))
(exwm-randr-refresh)
(shell-command "xrandr --output DP1-1 --right-of eDP1 --auto --primary"))
(defun randr-layout-hdmi1-extend ()
"Office layout for The Big Screen(tm)"
(interactive)
(setq exwm-randr-workspace-monitor-plist (generate-randr-config "HDMI1" "eDP1"))
(exwm-randr-refresh)
(shell-command "xrandr --output HDMI1 --dpi 144 --auto --right-of eDP1 --primary"))
(defun randr-layout-single ()
"Laptop screen only!"
(interactive)
(shell-command "xrandr --output HDMI1 --off")
(shell-command "xrandr --output DP1-1 --off")
(exwm-randr-refresh))
(defun set-xkb-layout (layout)
"Set the current X keyboard layout."
(shell-command (format "setxkbmap %s" layout))
(message "Set X11 keyboard layout to '%s'" layout))
(defun create-window-name ()
"Construct window names to be used for EXWM buffers by
inspecting the window's X11 class and title.
A lot of commonly used applications either create titles that
are too long by default, or in the case of web
applications (such as Cider) end up being constructed in
awkward ways.
To avoid this issue, some rewrite rules are applied for more
human-accessible titles."
(pcase (list (or exwm-class-name "unknown") (or exwm-title "unknown"))
;; In Cider windows, rename the class and keep the workspace/file
;; as the title.
(`("Google-chrome" ,(and (pred (lambda (title) (s-ends-with? " - Cider" title))) title))
(format "Cider<%s>" (s-chop-suffix " - Cider" title)))
;; Attempt to detect IRCCloud windows via their title, which is a
;; combination of the channel name and network.
;;
;; This is what would often be referred to as a "hack". The regexp
;; will not work if a network connection buffer is selected in
;; IRCCloud, but since the title contains no other indication that
;; we're dealing with an IRCCloud window
(`("Google-chrome"
,(and (pred (lambda (title)
(s-matches? "^[\*\+]\s#[a-zA-Z0-9/\-]+\s\|\s[a-zA-Z\.]+$" title)))
title))
(format "IRCCloud<%s>" title))
;; For other Chrome windows, make the title shorter.
(`("Google-chrome" ,title)
(format "Chrome<%s>" (s-truncate 42 (s-chop-suffix " - Google Chrome" title))))
;; Gnome-terminal -> Term
(`("Gnome-terminal" ,title)
;; fish-shell buffers contain some unnecessary whitespace and
;; such before the current working directory. This can be
;; stripped since most of my terminals are fish shells anyways.
(format "Term<%s>" (s-trim-left (s-chop-prefix "fish" title))))
;; For any other application, a name is constructed from the
;; window's class and name.
(`(,class ,title) (format "%s<%s>" class (s-truncate 12 title)))))
;; EXWM launch configuration
;;
;; This used to use use-package, but when something breaks use-package
;; it doesn't exactly make debugging any easier.
(let ((titlef (lambda ()
(exwm-workspace-rename-buffer (create-window-name)))))
(add-hook 'exwm-update-class-hook titlef)
(add-hook 'exwm-update-title-hook titlef))
(fringe-mode 3)
(exwm-enable)
;; 's-N': Switch to certain workspace
(setq exwm-workspace-number 10)
(dotimes (i 10)
(exwm-input-set-key (kbd (format "s-%d" i))
`(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
;; Launch applications / any command with completion (dmenu style!)
(exwm-input-set-key (kbd "s-d") #'counsel-linux-app)
(exwm-input-set-key (kbd "s-x") #'ivy-run-external-command)
(exwm-input-set-key (kbd "s-p") #'ivy-password-store)
;; Add X11 terminal selector to a key
(exwm-input-set-key (kbd "C-x t") #'counsel-switch-to-terminal)
;; 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)
;; Keyboard layouts (these are bound separately in Cyrillic
;; because I don't use reverse-im)
;; (-map
;; (lambda (pair)
;; (exwm-input-set-key
;; (kbd (format "s-%s" (cadr pair)))
;; `(lambda () (interactive) (set-xkb-layout ,(car pair)))))
;; '(("de" "k d")
;; ("de" "л в")
;; ("no" "k n")
;; ("no" "л т")
;; ("ru" "k r")
;; ("ru" "л к")
;; ("us" "k u")
;; ("us" "л г")))
;; Line-editing shortcuts
(exwm-input-set-simulation-keys
'(([?\C-d] . delete)
([?\C-w] . ?\C-c)))
;; Show time & battery status in the mode line
(display-time-mode)
(display-battery-mode)
;; enable display of X11 system tray within Emacs
(exwm-systemtray-enable)
;; Configure xrandr (multi-monitor setup)
(setq exwm-randr-workspace-monitor-plist (generate-randr-config "HDMI1" "eDP1"))
(exwm-randr-enable)
;; Let buffers move seamlessly between workspaces by making them
;; accessible in selectors on all frames.
(setq exwm-workspace-show-all-buffers t)
(setq exwm-layout-show-all-buffers t)
;; Monitor layouts
;;
;; TODO(tazjin): Desired layout should be inferred based on
;; connected screens - autorandr or something?
(exwm-input-set-key (kbd "s-m d") #'randr-layout-dp1-extend)
(exwm-input-set-key (kbd "s-m h") #'randr-layout-hdmi1-extend)
(exwm-input-set-key (kbd "s-m s") #'randr-layout-single)
(provide 'desktop)

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,206 @@
(require 's)
(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 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)))
(defvar external-command-flag-overrides
'(("google-chrome" . "--force-device-scale-factor=1.4"))
"This setting lets me add additional flags to specific commands
that are run interactively via `ivy-run-external-command'.")
(defun run-external-command (cmd)
"Execute the specified command and notify the user when it
finishes."
(let* ((extra-flags (cdr (assoc cmd external-command-flag-overrides)))
(cmd (if extra-flags (s-join " " (list cmd extra-flags)) cmd)))
(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 memespace-region ()
"Make a meme out of it."
(interactive)
(let* ((start (region-beginning))
(end (region-end))
(memed
(message
(s-trim-right
(apply #'string
(-flatten
(nreverse
(-reduce-from (lambda (acc x)
(cons (cons x (-repeat (+ 1 (length acc)) 32)) acc))
'()
(string-to-list (buffer-substring-no-properties start end))))))))))
(save-excursion (delete-region start end)
(goto-char start)
(insert memed))))
(provide 'functions)

160
tools/emacs/config/init.el Normal file
View file

@ -0,0 +1,160 @@
;;; 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)
;; 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 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 erlang
:hook ((erlang-mode . (lambda ()
;; Don't indent after '>' while I'm writing
(local-set-key ">" 'self-insert-command)))))
(use-package f)
(use-package go-mode
:bind (:map go-mode-map ("C-c C-r" . recompile)
:map go-mode-map ("<tab>" . company-indent-or-complete-common))
:hook ((go-mode . (lambda ()
(setq tab-width 2)
(setq-local compile-command
(concat "go build " buffer-file-name))))))
(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 lsp-mode)
(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)
;; 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)
;; Load all other Emacs configuration. These configurations are
;; added to `load-path' by Nix.
(mapc 'require '(desktop
mail-setup
look-and-feel
functions
settings
modes
bindings
term-setup
eshell-setup))
(telephone-line-setup)
(ace-window-display-mode)
;; If a local configuration file exists, it should be loaded. No
;; other configuration comes from `user-emacs-directory'.
(let ((local-file (expand-file-name (f-join user-emacs-directory "local.el"))))
(when (f-exists? local-file)
(load local-file)))
(provide 'init)

View file

@ -0,0 +1,132 @@
;;; -*- 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 modeline time in dottime (see https://dotti.me)
;;
;; This is done in a way that initially seems more complicated than
;; one would like, but this is unfortunately required due to the way
;; `format-time-string' handles timezones.
(defun format-dottime-advice (orig _ &optional _ _)
(let* ((offset-sec (car (current-time-zone)))
(offset-hours (/ offset-sec 60 60))
(dottime (if (/= offset-hours 0)
(concat "%M-%Dt%H·%M" (format "%0+3d" offset-hours))
"%m-%dT%H·%M")))
(apply orig '("%m-%dT%H·%M" nil t))))
(advice-add 'format-time-string :around #'format-dottime-advice)
;; 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 on 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))
;; TODO(tazjin): lets not do this particular thing while I
;; don't actually run notmuch, there are too many things
;; that have a dependency on the modeline drawing correctly
;; (including randr operations!)
;;
;; (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,90 @@
(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 "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")))
;; 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"))
(notmuch-count (format "I: %s; D: %s" inbox-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)

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)

View file

@ -0,0 +1,64 @@
(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)
;; 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,36 @@
;; Utilities for X11 terminal buffers.
(defvar x11-terminal-program "gnome-terminal"
"Which X11 terminal application to use.")
(defvar x11-terminal-buffer-prefix "Term"
"String prefix for X11 terminal buffer names.")
(defun open-or-create-terminal-buffer (buffer-name)
"Switch to the buffer with BUFFER-NAME or create a new buffer
running the configured X11 terminal."
(let ((buffer (get-buffer buffer-name)))
(if (not buffer)
(run-external-command x11-terminal-program)
(switch-to-buffer buffer))))
(defun is-terminal-buffer (buffer)
"Determine whether BUFFER runs an X11 terminal."
(and (equal 'exwm-mode (buffer-local-value 'major-mode buffer))
(s-starts-with? x11-terminal-buffer-prefix (buffer-name buffer))))
(defun counsel-switch-to-terminal ()
"Switch to an X11 terminal buffer, or create a new one."
(interactive)
(let ((terms (-map #'buffer-name
(-filter #'is-terminal-buffer (buffer-list)))))
(if terms
(ivy-read "Switch to terminal buffer: "
(cons "New terminal" terms)
:caller 'counsel-switch-to-terminal
:preselect (s-concat "^" x11-terminal-buffer-prefix)
:require-match t
:action #'open-or-create-terminal-buffer)
(run-external-command x11-terminal-program))))
(provide 'term-setup)