chore(tazjin/emacs): Move //tools/emacs to //users/tazjin/emacs

Change-Id: I9dfbd0bb1fd3e215bb91c5734fb0934ee3faeae6
This commit is contained in:
Vincent Ambo 2020-06-18 20:19:50 +01:00
parent 9448a2262b
commit 0b0e8c2da7
15 changed files with 2 additions and 2 deletions

View file

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

View file

@ -1,7 +0,0 @@
tools/emacs
===========
This sub-folder builds my Emacs configuration, supplying packages from
Nix and configuration from this folder.
I use Emacs for many things (including as my desktop environment).

View file

@ -1,54 +0,0 @@
;; Font size
(define-key global-map (kbd "C-=") 'increase-default-text-scale) ;; '=' because there lies '+'
(define-key global-map (kbd "C--") 'decrease-default-text-scale)
(define-key global-map (kbd "C-x C-0") 'set-default-text-scale)
;; What does <tab> do? Well, it depends ...
(define-key prog-mode-map (kbd "<tab>") #'company-indent-or-complete-common)
;; 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 C-p") 'ivy-browse-repositories)
(global-set-key (kbd "M-g M-g") 'goto-line-with-feedback)
;; Miscellaneous editing commands
(global-set-key (kbd "C-c w") 'whitespace-cleanup)
(global-set-key (kbd "C-c a") 'align-regexp)
(global-set-key (kbd "C-c m") 'mc/mark-dwim)
;; 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)
;; C-x REALLY QUIT (idea by @magnars)
(global-set-key (kbd "C-x r q") 'save-buffers-kill-terminal)
(global-set-key (kbd "C-x C-c") 'ignore)
;; 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)
;; Insert TODO comments
(global-set-key (kbd "C-c t") 'insert-todo-comment)
;; Add subthread collapsing to notmuch-show.
;;
;; C-, closes a thread, C-. opens a thread. This mirrors stepping
;; in/out of definitions.
(define-key notmuch-show-mode-map (kbd "C-,") 'notmuch-show-open-or-close-subthread)
(define-key notmuch-show-mode-map (kbd "C-.")
(lambda ()
(interactive)
(notmuch-show-open-or-close-subthread t))) ;; open
(provide 'bindings)

View file

@ -1,52 +0,0 @@
(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

@ -1,249 +0,0 @@
;; -*- 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 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") #'ts/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)
;; Shortcuts for switching between keyboard layouts
(defmacro bind-xkb (lang key)
`(exwm-input-set-key (kbd (format "s-%s" ,key))
(lambda ()
(interactive)
(set-xkb-layout ,lang))))
(bind-xkb "us" "k u")
(bind-xkb "de" "k d")
(bind-xkb "no" "k n")
(bind-xkb "ru" "k r")
;; These are commented out because Emacs no longer starts (??) if
;; they're set at launch.
;;
;; (bind-xkb "us" "л г")
;; (bind-xkb "de" "л в")
;; (bind-xkb "no" "л т")
;; (bind-xkb "ru" "л к")
;; 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).
;;
;; This makes some assumptions about how my machines are connected to
;; my home setup during the COVID19 isolation period.
(defun set-randr-config (screens)
(setq exwm-randr-workspace-monitor-plist
(-flatten (-map (lambda (screen)
(-map (lambda (screen-id) (list screen-id (car screen))) (cdr screen)))
screens))))
;; Layouts for Vauxhall (laptop)
(defun randr-vauxhall-layout-single ()
"Laptop screen only!"
(interactive)
(set-randr-config '(("eDP1" (number-sequence 0 9))))
(shell-command "xrandr --output eDP1 --auto --primary")
(shell-command "xrandr --output HDMI1 --off")
(shell-command "xrandr --output DP2 --off")
(exwm-randr-refresh))
(defun randr-vauxhall-layout-all ()
"Use all screens at home."
(interactive)
(set-randr-config
'(("eDP1" 0)
("HDMI1" 1 2 3 4 5)
("DP2" 6 7 8 9)))
(shell-command "xrandr --output HDMI1 --right-of eDP1 --auto --primary")
(shell-command "xrandr --output DP2 --right-of HDMI1 --auto")
(exwm-randr-refresh))
(defun randr-vauxhall-layout-wide-only ()
"Use only the wide screen at home."
(interactive)
(set-randr-config
'(("eDP1" 8 9 0)
("HDMI1" 1 2 4 5 6 7)))
(shell-command "xrandr --output DP2 --off")
(shell-command "xrandr --output HDMI1 --right-of eDP1 --auto --primary")
(exwm-randr-refresh))
;; Layouts for frog (desktop)
(defun randr-frog-layout-right-only ()
"Use only the right screen on frog."
(interactive)
(set-randr-config `(("DP-1" ,(number-sequence 0 9))))
(shell-command "xrandr --output DP-2 --off")
(shell-command "xrandr --output DP-1 --auto --primary"))
(defun randr-frog-layout-both ()
"Use the left and right screen on frog."
(interactive)
(set-randr-config `(("DP-2" 1 2 3 4 5)
("DP-1" 6 7 8 9 0)))
(shell-command "xrandr --output DP-2 --auto --primary --left-of DP-1")
(shell-command "xrandr --output DP-1 --auto --right-of DP-2"))
(pcase (s-trim (shell-command-to-string "hostname"))
("vauxhall"
(exwm-input-set-key (kbd "s-m s") #'randr-vauxhall-layout-single)
(exwm-input-set-key (kbd "s-m a") #'randr-vauxhall-layout-all)
(exwm-input-set-key (kbd "s-m w") #'randr-vauxhall-layout-wide-only))
("frog"
(exwm-input-set-key (kbd "s-m b") #'randr-frog-layout-both)
(exwm-input-set-key (kbd "s-m r") #'randr-frog-layout-right-only)))
(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)
(provide 'desktop)

View file

@ -1,68 +0,0 @@
;; 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

@ -1,287 +0,0 @@
(require 'chart)
(require 'dash)
(require 'map)
(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)))
;; 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 my monorepo in magit
(defun depot-status ()
(interactive)
(magit-status depot-path))
;; 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 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))))
(defhydra mc/mark-more-hydra (:color pink)
("<up>" mmlte--up "Mark previous like this")
("<down>" mc/mmlte--down "Mark next like this")
("<left>" mc/mmlte--left (if (eq mc/mark-more-like-this-extended-direction 'up)
"Skip past the cursor furthest up"
"Remove the cursor furthest down"))
("<right>" mc/mmlte--right (if (eq mc/mark-more-like-this-extended-direction 'up)
"Remove the cursor furthest up"
"Skip past the cursor furthest down"))
("f" nil "Finish selecting"))
;; Mute the message that mc/mmlte wants to print on its own
(advice-add 'mc/mmlte--message :around (lambda (&rest args) (ignore)))
(defun mc/mark-dwim (arg)
"Select multiple things, but do what I mean."
(interactive "p")
(if (not (region-active-p)) (mc/mark-next-lines arg)
(if (< 1 (count-lines (region-beginning)
(region-end)))
(mc/edit-lines arg)
;; The following is almost identical to `mc/mark-more-like-this-extended',
;; but uses a hydra (`mc/mark-more-hydra') instead of a transient key map.
(mc/mmlte--down)
(mc/mark-more-hydra/body))))
(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))))
(defun insert-todo-comment (prefix todo)
"Insert a comment at point with something for me to do."
(interactive "P\nsWhat needs doing? ")
(save-excursion
(move-end-of-line nil)
(insert (format " %s TODO(%s): %s"
(s-trim-right comment-start)
(if prefix (read-string "Who needs to do this? ")
(getenv "USER"))
todo))))
;; Custom text scale adjustment functions that operate on the entire instance
(defun modify-text-scale (factor)
(set-face-attribute 'default nil
:height (+ (* factor 5) (face-attribute 'default :height))))
(defun increase-default-text-scale (prefix)
"Increase default text scale in all Emacs frames, or just the
current frame if PREFIX is set."
(interactive "P")
(if prefix (text-scale-increase 1)
(modify-text-scale 1)))
(defun decrease-default-text-scale (prefix)
"Increase default text scale in all Emacs frames, or just the
current frame if PREFIX is set."
(interactive "P")
(if prefix (text-scale-decrease 1)
(modify-text-scale -1)))
(defun set-default-text-scale (prefix &optional to)
"Set the default text scale to the specified value, or the
default. Restores current frame's text scale only, if PREFIX is
set."
(interactive "P")
(if prefix (text-scale-adjust 0)
(set-face-attribute 'default nil :height (or to 120))))
(defun scrot-select ()
"Take a screenshot based on a mouse-selection and save it to
~/screenshots."
(interactive)
(shell-command "scrot '$a_%Y-%m-%d_%s.png' -s -e 'mv $f ~/screenshots/'"))
(defun graph-unread-mails ()
"Create a bar chart of unread mails based on notmuch tags.
Certain tags are excluded from the overview."
(interactive)
(let ((tag-counts
(-keep (-lambda ((name . search))
(let ((count
(string-to-number
(s-trim
(notmuch-command-to-string "count" search "and" "tag:unread")))))
(when (>= count 1) (cons name count))))
(notmuch-hello-generate-tag-alist '("unread" "signed" "attachment" "important")))))
(chart-bar-quickie
(if (< (length tag-counts) 6)
'vertical 'horizontal)
"Unread emails"
(-map #'car tag-counts) "Tag:"
(-map #'cdr tag-counts) "Count:")))
(defun notmuch-show-open-or-close-subthread (&optional prefix)
"Open or close the subthread from (and including) the message at point."
(interactive "P")
(save-excursion
(let ((current-depth (map-elt (notmuch-show-get-message-properties) :depth 0)))
(loop do (notmuch-show-message-visible (notmuch-show-get-message-properties) prefix)
until (or (not (notmuch-show-goto-message-next))
(= (map-elt (notmuch-show-get-message-properties) :depth) current-depth)))))
(force-window-update))
(defun vterm-send-ctrl-x ()
"Sends `C-x' to the libvterm."
(interactive)
(vterm-send-key "x" nil nil t))
(provide 'functions)

View file

@ -1,287 +0,0 @@
;;; 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))
:config
(setq aw-keys '(?f ?j ?d ?k ?s ?l ?a)
aw-scope 'frame))
(use-package auth-source-pass :config (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))
:config (setq company-tooltip-align-annotations t))
(use-package counsel
:after (ivy)
:config (counsel-mode 1)
:bind (("C-c r g" . counsel-rg)))
(use-package dash)
(use-package dash-functional)
(use-package dottime
:demand
:after (notmuch telega)
:config (dottime-display-mode t))
(use-package gruber-darker-theme)
(use-package eglot
:custom
(eglot-autoshutdown t)
(eglot-send-changes-idle-time 0.3))
(use-package ht)
(use-package hydra)
(use-package idle-highlight-mode :hook ((prog-mode . idle-highlight-mode)))
(use-package ivy
:config
(ivy-mode 1)
(setq enable-recursive-minibuffers t)
(setq ivy-use-virtual-buffers t))
(use-package ivy-pass :after (ivy))
(use-package ivy-prescient
:after (ivy prescient)
:config
(ivy-prescient-mode)
;; Fixes an issue with how regexes are passed to ripgrep from counsel,
;; see raxod502/prescient.el#43
(setf (alist-get 'counsel-rg ivy-re-builders-alist) #'ivy--regex-plus))
(use-package multiple-cursors)
(use-package notmuch
:bind (:map global-map
("s-g m" . notmuch)
("s-g M" . counsel-notmuch)) ;; g m -> gmail
:config
(setq notmuch-search-oldest-first nil)
(setq notmuch-show-all-tags-list t)
(setq notmuch-hello-tag-list-make-query "tag:unread"))
(use-package paredit :hook ((lisp-mode . paredit-mode)
(emacs-lisp-mode . paredit-mode)))
(use-package pinentry
:config
(setq epa-pinentry-mode 'loopback)
(pinentry-start))
(use-package prescient
:after (ivy counsel)
:config (prescient-persist-mode))
(use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode))
(use-package rainbow-mode)
(use-package s)
(use-package string-edit)
(use-package swiper
:after (counsel ivy)
:bind (("C-s" . swiper)))
(use-package telephone-line) ;; configuration happens outside of use-package
(use-package term-switcher)
(use-package undo-tree :config (global-undo-tree-mode))
(use-package uuidgen)
(use-package which-key :config (which-key-mode t))
;;
;; Applications in emacs
;;
(use-package magit
:bind ("C-c g" . magit-status)
:config (setq magit-repository-directories '(("/home/tazjin/projects" . 2)
("/home/tazjin" . 1))))
(use-package org-journal
;; Always use my own key to encrypt files. There seems to be no
;; global way to set this, as `epa-file-encrypt-to' only has an
;; effect as a file-local variable (?!)
:hook ((org-journal-mode . (lambda ()
(setq-local epa-file-encrypt-to
"DCF34CFAC1AC44B87E26333136EE34814F6D294A"))))
:config
(setq org-journal-dir "/ssh:camden.tazj.in:/home/tazjin/journal"
org-journal-encrypt-journal t
org-journal-file-type 'weekly
org-journal-date-format "%A, %Y-%m-%d"
org-journal-file-format "%Y%m%d-weekly"
;; Saturday, because reasons.
org-journal-start-on-weekday 6)
;; org-journal doesn't actually enter its mode automatically if
;; encryption is used (I'm not sure why), so this teaches Emacs to
;; recognise the files.
(add-to-list 'auto-mode-alist '("[0-9]-weekly\\.gpg\\'" . org-journal-mode)))
(use-package org-ql)
(use-package password-store)
(use-package pg)
(use-package restclient)
(use-package vterm
:config (progn
(setq vterm-shell "fish")
(setq vterm-exit-functions
(lambda (&rest _) (kill-buffer (current-buffer))))
(setq vterm-set-title-functions
(lambda (title)
(rename-buffer
(generate-new-buffer-name
(format "vterm<%s>"
(s-trim-left
(s-chop-prefix "fish" title)))))))))
;;
;; 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 geiser)
(use-package go-mode
:bind (:map go-mode-map ("C-c C-r" . recompile))
:hook ((go-mode . (lambda ()
(setq tab-width 2)
(setq-local compile-command
(concat "go build " buffer-file-name))))))
(use-package haskell-mode)
(use-package ielm
:hook ((inferior-emacs-lisp-mode . (lambda ()
(paredit-mode)
(rainbow-delimiters-mode-enable)
(company-mode)))))
(use-package jq-mode
:config (add-to-list 'auto-mode-alist '("\\.jq\\'" . jq-mode)))
(use-package kotlin-mode
:hook ((kotlin-mode . (lambda ()
(setq indent-line-function #'indent-relative)))))
(use-package lsp-mode)
(use-package markdown-mode
:config
(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
:hook ((nix-mode . (lambda ()
(setq indent-line-function #'nix-indent-line)))))
(use-package nix-util)
(use-package nginx-mode)
(use-package rust-mode)
(use-package sly
:hook ((sly-mrepl-mode . (lambda ()
(paredit-mode)
(rainbow-delimiters-mode-enable)
(company-mode))))
:config
(setq common-lisp-hyperspec-root "file:///home/tazjin/docs/lisp/"))
(use-package telega
:bind (:map global-map ("s-t" . telega))
:config (telega-mode-line-mode 1))
(use-package terraform-mode)
(use-package toml-mode)
(use-package web-mode)
(use-package yaml-mode)
(defgroup tazjin nil
"Settings related to my configuration")
(defcustom depot-path "/depot"
"Local path to the depot checkout"
:group 'tazjin)
;; Configuration changes in `customize` can not actually be persisted
;; to the customise file that Emacs is currently using (since it comes
;; from the Nix store).
;;
;; The way this will work for now is that Emacs will *write*
;; configuration to the file tracked in my repository, while not
;; actually *reading* it from there (unless Emacs is rebuilt).
(setq custom-file (expand-file-name "~/depot/tools/emacs/config/custom.el"))
(load-library "custom")
(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
eshell-setup))
(telephone-line-setup)
(ace-window-display-mode)
;; If a local configuration library exists, it should be loaded.
;;
;; This can be provided by calling my Emacs derivation with
;; `withLocalConfig'.
(if-let (local-file (locate-library "local"))
(load local-file))
(provide 'init)

View file

@ -1,113 +0,0 @@
;;; -*- 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 Emacs fonts.
(let ((font (format "JetBrains Mono-%d" 12)))
(setq default-frame-alist `((font . ,font)))
(set-frame-font font t t))
;; 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)
(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

@ -1,83 +0,0 @@
(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)
(setq notmuch-fcc-dirs nil) ;; Gmail does this server-side
(setq message-signature nil) ;; Insert message signature manually with C-c C-w
;; 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)
;; 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)
;; 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

@ -1,37 +0,0 @@
;; Initializes modes I use.
(add-hook 'prog-mode-hook 'esk-add-watchwords)
(add-hook 'prog-mode-hook 'hl-line-mode)
;; 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
(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)
;; Configure go-mode for Go2 Alpha
(add-to-list 'auto-mode-alist '("\\.go2$" . go-mode))
(provide 'modes)

View file

@ -1,51 +0,0 @@
(require 'uniquify)
;; 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
initial-major-mode 'emacs-lisp-mode)
(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

@ -1,147 +0,0 @@
# This file builds an Emacs pre-configured with the packages I need
# and my personal Emacs configuration.
#
# On NixOS machines, this Emacs currently does not support
# Imagemagick, see https://github.com/NixOS/nixpkgs/issues/70631.
#
# Forcing Emacs to link against Imagemagick currently causes libvterm
# to segfault, which is a lot less desirable than not having telega
# render images correctly.
{ depot, lib, ... }:
let
inherit (depot) third_party;
emacsWithPackages = (third_party.emacsPackagesGen third_party.emacs26).emacsWithPackages;
# $PATH for binaries that need to be available to Emacs
emacsBinPath = lib.makeBinPath [ third_party.telega ];
identity = x: x;
tazjinsEmacs = pkgfun: (emacsWithPackages(epkgs: pkgfun(
# Actual ELPA packages (the enlightened!)
(with epkgs.elpaPackages; [
ace-window
avy
flymake
pinentry
rainbow-mode
undo-tree
xelb
]) ++
# MELPA packages:
(with epkgs.melpaPackages; [
ace-link
browse-kill-ring
cargo
clojure-mode
cmake-mode
counsel
counsel-notmuch
dash-functional
direnv
dockerfile-mode
eglot
elixir-mode
elm-mode
erlang
geiser
go-mode
gruber-darker-theme
haskell-mode
ht
hydra
idle-highlight-mode
intero
ivy
ivy-pass
ivy-prescient
jq-mode
kotlin-mode
lispy
lsp-mode
magit
markdown-toc
meson-mode
multi-term
multiple-cursors
nginx-mode
nix-mode
notmuch # this comes from pkgs.third_party
org-journal
org-ql
paredit
password-store
pg
polymode
prescient
protobuf-mode
racket-mode
rainbow-delimiters
refine
request
restclient
sly
string-edit
swiper
telega
telephone-line
terraform-mode
toml-mode
transient
use-package
uuidgen
web-mode
websocket
which-key
yaml-mode
yasnippet
]) ++
# Custom packages
(with depot.tools.emacs-pkgs; [
dottime
nix-util
term-switcher
# patched / overridden versions of packages
depot.third_party.emacs.exwm
depot.third_party.emacs.rcirc
depot.third_party.emacs.vterm
depot.third_party.emacs.explain-pause-mode
]))));
in lib.fix(self: l: f: third_party.writeShellScriptBin "tazjins-emacs" ''
export PATH="${emacsBinPath}:$PATH"
exec ${tazjinsEmacs f}/bin/emacs \
--debug-init \
--no-site-file \
--no-site-lisp \
--no-init-file \
--directory ${./config} ${if l != null then "--directory ${l}" else ""} \
--eval "(require 'init)" $@
'' // {
# Call overrideEmacs with a function (pkgs -> pkgs) to modify the
# packages that should be included in this Emacs distribution.
overrideEmacs = f': self l f';
# Call withLocalConfig with the path to a *folder* containing a
# `local.el` which provides local system configuration.
withLocalConfig = confDir: self confDir f;
# Build a derivation that uses the specified local Emacs (i.e.
# built outside of Nix) instead
withLocalEmacs = emacsBin: third_party.writeShellScriptBin "tazjins-emacs" ''
export PATH="${emacsBinPath}:$PATH"
export EMACSLOADPATH="${(tazjinsEmacs f).deps}/share/emacs/site-lisp:"
exec ${emacsBin} \
--debug-init \
--no-site-file \
--no-site-lisp \
--no-init-file \
--directory ${./config} \
${if l != null then "--directory ${l}" else ""} \
--eval "(require 'init)" $@
'';
}) null identity