From 0b0e8c2da77979eaea9e0b0da865d35bd86936d5 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 18 Jun 2020 20:19:50 +0100 Subject: chore(tazjin/emacs): Move //tools/emacs to //users/tazjin/emacs Change-Id: I9dfbd0bb1fd3e215bb91c5734fb0934ee3faeae6 --- ci-builds.nix | 2 +- tools/emacs/.gitignore | 11 -- tools/emacs/README.md | 7 - tools/emacs/config/bindings.el | 54 ------ tools/emacs/config/custom.el | 52 ------ tools/emacs/config/desktop.el | 249 ------------------------- tools/emacs/config/eshell-setup.el | 68 ------- tools/emacs/config/functions.el | 287 ----------------------------- tools/emacs/config/init.el | 287 ----------------------------- tools/emacs/config/look-and-feel.el | 113 ------------ tools/emacs/config/mail-setup.el | 83 --------- tools/emacs/config/modes.el | 37 ---- tools/emacs/config/settings.el | 51 ----- tools/emacs/default.nix | 147 --------------- users/tazjin/emacs/.gitignore | 11 ++ users/tazjin/emacs/README.md | 7 + users/tazjin/emacs/config/bindings.el | 54 ++++++ users/tazjin/emacs/config/custom.el | 52 ++++++ users/tazjin/emacs/config/desktop.el | 249 +++++++++++++++++++++++++ users/tazjin/emacs/config/eshell-setup.el | 68 +++++++ users/tazjin/emacs/config/functions.el | 287 +++++++++++++++++++++++++++++ users/tazjin/emacs/config/init.el | 287 +++++++++++++++++++++++++++++ users/tazjin/emacs/config/look-and-feel.el | 113 ++++++++++++ users/tazjin/emacs/config/mail-setup.el | 83 +++++++++ users/tazjin/emacs/config/modes.el | 37 ++++ users/tazjin/emacs/config/settings.el | 51 +++++ users/tazjin/emacs/default.nix | 147 +++++++++++++++ users/tazjin/nixos/frog/default.nix | 2 +- 28 files changed, 1448 insertions(+), 1448 deletions(-) delete mode 100644 tools/emacs/.gitignore delete mode 100644 tools/emacs/README.md delete mode 100644 tools/emacs/config/bindings.el delete mode 100644 tools/emacs/config/custom.el delete mode 100644 tools/emacs/config/desktop.el delete mode 100644 tools/emacs/config/eshell-setup.el delete mode 100644 tools/emacs/config/functions.el delete mode 100644 tools/emacs/config/init.el delete mode 100644 tools/emacs/config/look-and-feel.el delete mode 100644 tools/emacs/config/mail-setup.el delete mode 100644 tools/emacs/config/modes.el delete mode 100644 tools/emacs/config/settings.el delete mode 100644 tools/emacs/default.nix create mode 100644 users/tazjin/emacs/.gitignore create mode 100644 users/tazjin/emacs/README.md create mode 100644 users/tazjin/emacs/config/bindings.el create mode 100644 users/tazjin/emacs/config/custom.el create mode 100644 users/tazjin/emacs/config/desktop.el create mode 100644 users/tazjin/emacs/config/eshell-setup.el create mode 100644 users/tazjin/emacs/config/functions.el create mode 100644 users/tazjin/emacs/config/init.el create mode 100644 users/tazjin/emacs/config/look-and-feel.el create mode 100644 users/tazjin/emacs/config/mail-setup.el create mode 100644 users/tazjin/emacs/config/modes.el create mode 100644 users/tazjin/emacs/config/settings.el create mode 100644 users/tazjin/emacs/default.nix diff --git a/ci-builds.nix b/ci-builds.nix index 01f014da49..1f4f967656 100644 --- a/ci-builds.nix +++ b/ci-builds.nix @@ -24,12 +24,12 @@ with (import ./default.nix {}); [ third_party.lisp # will build all third-party libraries third_party.nix tools.cheddar - tools.emacs web.blog web.cgit-taz web.tvl # tazjin's personal things + users.tazjin.emacs users.tazjin.nixos.camdenSystem users.tazjin.nixos.frogSystem ] diff --git a/tools/emacs/.gitignore b/tools/emacs/.gitignore deleted file mode 100644 index 7b666905f8..0000000000 --- a/tools/emacs/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.smex-items -*token* -auto-save-list/ -clones/ -elpa/ -irc.el -local.el -other/ -scripts/ -themes/ -*.elc diff --git a/tools/emacs/README.md b/tools/emacs/README.md deleted file mode 100644 index 5c66733396..0000000000 --- a/tools/emacs/README.md +++ /dev/null @@ -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). diff --git a/tools/emacs/config/bindings.el b/tools/emacs/config/bindings.el deleted file mode 100644 index f66a6ab551..0000000000 --- a/tools/emacs/config/bindings.el +++ /dev/null @@ -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 do? Well, it depends ... -(define-key prog-mode-map (kbd "") #'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) diff --git a/tools/emacs/config/custom.el b/tools/emacs/config/custom.el deleted file mode 100644 index a157c7a5fa..0000000000 --- a/tools/emacs/config/custom.el +++ /dev/null @@ -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"))))) diff --git a/tools/emacs/config/desktop.el b/tools/emacs/config/desktop.el deleted file mode 100644 index b5915233cd..0000000000 --- a/tools/emacs/config/desktop.el +++ /dev/null @@ -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 "") #'volume-mute) -(exwm-input-set-key (kbd "") #'volume-up) -(exwm-input-set-key (kbd "") #'volume-down) - -;; Brightness keys -(exwm-input-set-key (kbd "") #'brightness-down) -(exwm-input-set-key (kbd "") #'brightness-up) -(exwm-input-set-key (kbd "") #'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) diff --git a/tools/emacs/config/eshell-setup.el b/tools/emacs/config/eshell-setup.el deleted file mode 100644 index 0b23c5a2d1..0000000000 --- a/tools/emacs/config/eshell-setup.el +++ /dev/null @@ -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) diff --git a/tools/emacs/config/functions.el b/tools/emacs/config/functions.el deleted file mode 100644 index 907dbe5e11..0000000000 --- a/tools/emacs/config/functions.el +++ /dev/null @@ -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 {}; " 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 ): "))) - (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) - ("" mmlte--up "Mark previous like this") - ("" mc/mmlte--down "Mark next like this") - ("" mc/mmlte--left (if (eq mc/mark-more-like-this-extended-direction 'up) - "Skip past the cursor furthest up" - "Remove the cursor furthest down")) - ("" 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) diff --git a/tools/emacs/config/init.el b/tools/emacs/config/init.el deleted file mode 100644 index b5d570df09..0000000000 --- a/tools/emacs/config/init.el +++ /dev/null @@ -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) diff --git a/tools/emacs/config/look-and-feel.el b/tools/emacs/config/look-and-feel.el deleted file mode 100644 index 5a4d874f6f..0000000000 --- a/tools/emacs/config/look-and-feel.el +++ /dev/null @@ -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) diff --git a/tools/emacs/config/mail-setup.el b/tools/emacs/config/mail-setup.el deleted file mode 100644 index 1167bcadd3..0000000000 --- a/tools/emacs/config/mail-setup.el +++ /dev/null @@ -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) diff --git a/tools/emacs/config/modes.el b/tools/emacs/config/modes.el deleted file mode 100644 index 69fb523d0d..0000000000 --- a/tools/emacs/config/modes.el +++ /dev/null @@ -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) diff --git a/tools/emacs/config/settings.el b/tools/emacs/config/settings.el deleted file mode 100644 index b895d5e406..0000000000 --- a/tools/emacs/config/settings.el +++ /dev/null @@ -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) diff --git a/tools/emacs/default.nix b/tools/emacs/default.nix deleted file mode 100644 index 5d492853cf..0000000000 --- a/tools/emacs/default.nix +++ /dev/null @@ -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 diff --git a/users/tazjin/emacs/.gitignore b/users/tazjin/emacs/.gitignore new file mode 100644 index 0000000000..7b666905f8 --- /dev/null +++ b/users/tazjin/emacs/.gitignore @@ -0,0 +1,11 @@ +.smex-items +*token* +auto-save-list/ +clones/ +elpa/ +irc.el +local.el +other/ +scripts/ +themes/ +*.elc diff --git a/users/tazjin/emacs/README.md b/users/tazjin/emacs/README.md new file mode 100644 index 0000000000..5c66733396 --- /dev/null +++ b/users/tazjin/emacs/README.md @@ -0,0 +1,7 @@ +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). diff --git a/users/tazjin/emacs/config/bindings.el b/users/tazjin/emacs/config/bindings.el new file mode 100644 index 0000000000..f66a6ab551 --- /dev/null +++ b/users/tazjin/emacs/config/bindings.el @@ -0,0 +1,54 @@ +;; 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 do? Well, it depends ... +(define-key prog-mode-map (kbd "") #'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) diff --git a/users/tazjin/emacs/config/custom.el b/users/tazjin/emacs/config/custom.el new file mode 100644 index 0000000000..a157c7a5fa --- /dev/null +++ b/users/tazjin/emacs/config/custom.el @@ -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"))))) diff --git a/users/tazjin/emacs/config/desktop.el b/users/tazjin/emacs/config/desktop.el new file mode 100644 index 0000000000..b5915233cd --- /dev/null +++ b/users/tazjin/emacs/config/desktop.el @@ -0,0 +1,249 @@ +;; -*- 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 "") #'volume-mute) +(exwm-input-set-key (kbd "") #'volume-up) +(exwm-input-set-key (kbd "") #'volume-down) + +;; Brightness keys +(exwm-input-set-key (kbd "") #'brightness-down) +(exwm-input-set-key (kbd "") #'brightness-up) +(exwm-input-set-key (kbd "") #'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) diff --git a/users/tazjin/emacs/config/eshell-setup.el b/users/tazjin/emacs/config/eshell-setup.el new file mode 100644 index 0000000000..0b23c5a2d1 --- /dev/null +++ b/users/tazjin/emacs/config/eshell-setup.el @@ -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) diff --git a/users/tazjin/emacs/config/functions.el b/users/tazjin/emacs/config/functions.el new file mode 100644 index 0000000000..907dbe5e11 --- /dev/null +++ b/users/tazjin/emacs/config/functions.el @@ -0,0 +1,287 @@ +(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 {}; " 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 ): "))) + (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) + ("" mmlte--up "Mark previous like this") + ("" mc/mmlte--down "Mark next like this") + ("" mc/mmlte--left (if (eq mc/mark-more-like-this-extended-direction 'up) + "Skip past the cursor furthest up" + "Remove the cursor furthest down")) + ("" 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) diff --git a/users/tazjin/emacs/config/init.el b/users/tazjin/emacs/config/init.el new file mode 100644 index 0000000000..b5d570df09 --- /dev/null +++ b/users/tazjin/emacs/config/init.el @@ -0,0 +1,287 @@ +;;; 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) diff --git a/users/tazjin/emacs/config/look-and-feel.el b/users/tazjin/emacs/config/look-and-feel.el new file mode 100644 index 0000000000..5a4d874f6f --- /dev/null +++ b/users/tazjin/emacs/config/look-and-feel.el @@ -0,0 +1,113 @@ +;;; -*- 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) diff --git a/users/tazjin/emacs/config/mail-setup.el b/users/tazjin/emacs/config/mail-setup.el new file mode 100644 index 0000000000..1167bcadd3 --- /dev/null +++ b/users/tazjin/emacs/config/mail-setup.el @@ -0,0 +1,83 @@ +(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) diff --git a/users/tazjin/emacs/config/modes.el b/users/tazjin/emacs/config/modes.el new file mode 100644 index 0000000000..69fb523d0d --- /dev/null +++ b/users/tazjin/emacs/config/modes.el @@ -0,0 +1,37 @@ +;; 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) diff --git a/users/tazjin/emacs/config/settings.el b/users/tazjin/emacs/config/settings.el new file mode 100644 index 0000000000..b895d5e406 --- /dev/null +++ b/users/tazjin/emacs/config/settings.el @@ -0,0 +1,51 @@ +(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) diff --git a/users/tazjin/emacs/default.nix b/users/tazjin/emacs/default.nix new file mode 100644 index 0000000000..5d492853cf --- /dev/null +++ b/users/tazjin/emacs/default.nix @@ -0,0 +1,147 @@ +# 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 diff --git a/users/tazjin/nixos/frog/default.nix b/users/tazjin/nixos/frog/default.nix index 7da724d9cf..3b99f082f5 100644 --- a/users/tazjin/nixos/frog/default.nix +++ b/users/tazjin/nixos/frog/default.nix @@ -9,7 +9,7 @@ config: let # add google-c-style here because other machines get it from, eh, # elsewhere. - frogEmacs = (depot.tools.emacs.overrideEmacs(epkgs: epkgs ++ [ + frogEmacs = (depot.users.tazjin.emacs.overrideEmacs(epkgs: epkgs ++ [ depot.third_party.emacsPackages.google-c-style ])); in depot.lib.fix(self: { -- cgit 1.4.1