;; -*- lexical-binding: t -*- ;;; UI customization ;;; (column-number-mode) (electric-pair-mode) (global-so-long-mode) (menu-bar-mode -1) (scroll-bar-mode -1) (show-paren-mode) (tool-bar-mode -1) (setq frame-title-format "%b — Emacs") (setq uniquify-buffer-name-style 'forward) (setq-default truncate-lines t) (load-theme 'monokai t) ;; <102 or >109 will make Ioveska look squished rather than tall, ;; narrow, and beautiful. (set-face-attribute 'default nil :height 104) ;; Disabling bidirectional text will make Emacs less slow with very ;; long lines. (setq bidi-paragraph-direction 'left-to-right) (setq bidi-inhibit-bpa t) ;;; Utility functions ;;; ;; Set an environment variable value for a single buffer. (defun setenv-local (variable &optional value substitute-env-vars) ;; If process-environment is the global one, make a copy of it. (when (eq process-environment (default-value 'process-environment)) (make-local-variable 'process-environment) (setq process-environment (mapcar 'concat process-environment))) (setenv variable value substitute-env-vars)) (defun enable-ansi-color () (setenv-local "TERM" "dumb-emacs-ansi")) ;;; Major modes ;;; (add-to-list 'auto-mode-alist '("\\.adoc\\'" . adoc-mode)) (add-to-list 'auto-mode-alist '("\\.quirks\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("\\.gni?\\'" . gn-mode)) (add-to-list 'auto-mode-alist '("/muttrc\\'" . mutt-mode)) (add-to-list 'auto-mode-alist '("\\.tmac\\'" . nroff-mode)) (add-to-list 'auto-mode-alist '("\\.wrap\\'" . conf-unix-mode)) (add-to-list 'auto-mode-alist '("/Cargo\\.lock\\'" . toml-mode)) (add-to-list 'auto-mode-alist '("/\\.clang-format\\'" . yaml-mode)) (add-to-list 'auto-mode-alist '("/up\\'" . execline-mode)) (add-to-list 'auto-mode-alist '("/down\\'" . execline-mode)) ;;; auth-source ;;; (auth-source-pass-enable) ;;; Backups ;;; (let ((backup-dir (concat (file-name-as-directory user-emacs-directory) "backups"))) (make-directory backup-dir t) (setq backup-directory-alist `(("." . ,backup-dir))) (setq message-auto-save-directory backup-dir)) ;;; CC Mode ;;; (with-eval-after-load 'cc-styles ;; Default to kernel style. (add-to-list 'c-default-style '(other . "linux"))) ;;; Comint ;;; ;; This relies on an Emacs patch to apply to async-shell-command. (setq comint-terminfo-terminal "dumb-emacs-ansi") ;;; Diff Mode ;;; ;; Unbind M-DEL from scroll-down-command, so it does the same thing as ;; in the rest of Emacs (backward-kill-word). (with-eval-after-load 'diff-mode (define-key diff-mode-map (kbd "M-DEL") nil)) ;;; Dired ;;; (with-eval-after-load 'dired ;; Use human-readable sizes. (setq dired-listing-switches (combine-and-quote-strings `(,dired-listing-switches "-h"))) ;; Don't open new windows when clicking directory entries. (define-key dired-mode-map [mouse-2] 'dired-find-file)) ;;; Direnv ;;; (direnv-mode) (global-set-key (kbd "C-c d") #'direnv-update-environment) ;;; Ediff ;;; ;; Don't make a new frame for Ediff controls. (setq ediff-window-setup-function 'ediff-setup-windows-plain) ;;; EditorConfig ;;; (editorconfig-mode) ;;; Eglot ;;; (with-eval-after-load 'eglot ;; Override default LSPs for languages. (add-to-list 'eglot-server-programs '((c++-mode c-mode) "clangd")) (add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer")) (add-to-list 'eglot-server-programs '(nix-mode "rnix-lsp")) ;; Underline all occurrences of the symbol at point in the current ;; buffer (default is to bold instead). (set-face-attribute 'eglot-highlight-symbol-face nil :inherit 'underline) ;; Add keybindings for eglot actions. (define-key eglot-mode-map (kbd "C-c e a") #'eglot-code-actions) (define-key eglot-mode-map (kbd "C-c e r") #'eglot-rename)) ;; Tell rust-analyzer not to allow colons inside Rust import. (setq-default eglot-workspace-configuration '((:rust-analyzer . (:assist (:importMergeBehaviour "last"))))) ;;; Environment ;;; ;; Provide a way to turn paging back on for modes like terminal emulators. (let ((pager (getenv "PAGER"))) (defun enable-pager () (setenv-local "PAGER" pager))) ;; Set PAGER to the empty string, which Git and journalctl will ;; interpret as an explicit opt-out of paging. (setenv "PAGER" "") ;;; Eshell ;;; (add-hook 'eshell-mode-hook #'enable-ansi-color) ;;; gdb-mi ;;; (with-eval-after-load "gdb-mi" ;; Hide all the copyright and documentation messages at gdb startup. (setq gud-gdb-command-name (concat gud-gdb-command-name " -q"))) ;;; Ibuffer ;;; ;; Open ibuffer in other window, to match behaviour of the default ;; list-buffers. (global-set-key (kbd "C-x C-b") (lambda () (interactive) (ibuffer t))) ;;; ispell ;;; ;; System locale is Esperanto, but I write in English much more, and ;; spelling in Esperanto is easy anyway. ;) (setq ispell-dictionary "english") ;;; Magit ;;; (setq magit-delete-by-moving-to-trash delete-by-moving-to-trash) (setq magit-repository-directories `((,(expand-file-name "~/src") . 1))) (global-set-key (kbd "C-x g") #'magit-status) (global-set-key (kbd "C-x M-g") #'magit-dispatch) (with-eval-after-load "git-commit" (add-to-list 'git-commit-trailers "Change-Id") (add-to-list 'git-commit-trailers "Fixes") (add-to-list 'git-commit-trailers "Message-Id")) ;; Don't open a second window with a diff when committing. I have ;; commit.verbose set in git, so the diff is shown below the commit ;; anyway, and it's annoying to have magit take over the whole frame ;; because it means I can't refer to something else, like an email ;; thread discussing the commit. And sometimes the diff shown by ;; magit doesn't agree with the diff shown by git (and git is always ;; in the right when this happens). (setq magit-commit-show-diff nil) (defun git-commit-message-pretty-ref (commit) "Generate a reference to git COMMIT in the format used in the Linux kernel (`SHORT-HASH (\"SHORT-MESSAGE\")') and insert it at point." (interactive (list (or (nreverse (magit-region-values 'commit)) (magit-read-other-branch-or-commit "Reference commit")))) (call-process (magit-git-executable) nil t nil "show" "--no-patch" "--pretty=format:%h (\"%s\")" commit)) (with-eval-after-load "magit" (define-key magit-process-mode-map (kbd "k") #'magit-process-kill) ;; Add a --no-gpg-sign option to Magit. This is only useful with ;; commit.gpgsign=true. It would be nice if commit.gpgsign=true just ;; meant that Magit showed the --gpg-sign option as enabled by ;; default, and disabling it would make Magit pass --no-gpg-sign to ;; git, but that's not currently the case, so we need a seperate ;; option for --no-gpg-sign. See ;; . (dolist (command '(magit-commit magit-merge magit-cherry-pick magit-revert magit-am magit-rebase)) (transient-append-suffix command ["-S"] '("=S" "Don't sign using gpg" "--no-gpg-sign")))) ;;; Markdown ;;; (setq-default markdown-hide-markup t) ;;; Man ;;; ;; Open man pages in the current window. I don't really care what ;; happens when I do M-x man, but I don't want my whole frame to be ;; taken over with man buffers if I'm following a bunch of ;; cross-references. (setq Man-notify-method 'pushy) ;;; MML ;;; (setq mml-secure-openpgp-encrypt-to-self t) (setq mml-secure-openpgp-signers '("757356D779BBB888773E415E736CCDF9EF51BD97")) (add-hook 'message-setup-hook 'mml-secure-message-sign-pgpmime) ;;; Nix support ;;; (defun browse-url-nixpkgs (attr) "Open a browser to the homepage for the Nixpkgs attribute ATTR." (interactive "sAttribute: ") (let* ((full-attr-quoted (shell-quote-argument (concat attr ".meta.homepage"))) (command (concat "nix --extra-experimental-features nix-command eval --raw -f '' " full-attr-quoted)) (homepage (shell-command-to-string command))) (browse-url homepage))) ;;; notmuch ;;; (setq notmuch-search-oldest-first nil) (setq notmuch-fcc-dirs "Sent") (setq notmuch-draft-folder "Drafts") (setq notmuch-saved-searches `((:name "direct" :query ,(concat "-tag:done -folder:Spam -from:discourse@discourse.nixos.org to:" (getenv "EMAIL")) :key "d") (:name "github" :query "-tag:done -to:your_activity@noreply.github.com from:notifications@github.com" :key "g") (:name "lists" :query "-tag:done to:afra@afra-berlin.de OR to:@list.skarnet.org OR wayland-devel@lists.freedesktop.org OR to:distributions@lists.linux.dev OR to:virglrenderer-devel@lists.freedesktop.org OR to:config-patches@gnu.org OR to:linux-kernel-announce@vger.kernel.org" :key "l"))) (setq notmuch-tagging-keys '(("d" ("+done") "done"))) ;;; mail ;;; ; This has to go after we set the notmuch settings, for some reason, ; or they are not applied. (require 'notmuch-mua) (setq mail-user-agent 'notmuch-user-agent) ;;; Org-mode ;;; (setq org-id-locations-file (concat user-emacs-directory "/org-id-locations")) ;;; Org-roam ;;; (setq org-roam-directory "~/notes") (setq org-roam-v2-ack t) (org-roam-db-autosync-mode) (global-set-key (kbd "C-c o c") #'org-roam-capture) (global-set-key (kbd "C-c o f") #'org-roam-node-find) (define-key org-mode-map (kbd "C-c o i") #'org-roam-node-insert) (define-key org-mode-map (kbd "C-c o r") #'org-roam-ref-add) ;;; Rainbow Delimiters ;;; (add-hook 'prog-mode-hook #'rainbow-delimiters-mode) ;;; Revert buffers ;;; (global-set-key (kbd "C-c r") #'revert-buffer) ;;; Ruby ;;; (setq ruby-align-to-stmt-keywords t) ;;; Rust ;;; ;; Indent with spaces in Rust code. (add-hook 'rust-mode-hook (lambda () (setq indent-tabs-mode nil))) (with-eval-after-load 'rust-mode ;; Cargo keybindings (define-key rust-mode-map (kbd "C-c c b") 'rust-compile) (define-key rust-mode-map (kbd "C-c c c") 'rust-check) (define-key rust-mode-map (kbd "C-c c r") 'rust-run) (define-key rust-mode-map (kbd "C-c c t") 'rust-test)) ;;; save-some-buffers ;;; ;; Allow reverting buffers directly from the "Save file ...?" message. (add-to-list 'save-some-buffers-action-alist `(?r ,(lambda (buf) (with-current-buffer buf (revert-buffer t t))) ,(purecopy "revert this buffer"))) ;;; sendmail ;;; ;; Use the system "sendmail" program to send mail. (setq mail-envelope-from 'header) (setq mail-specify-envelope-from t) (setq send-mail-function 'sendmail-send-it) ;;; Term ;;; (add-hook 'term-mode-hook #'enable-pager) ;;; Transient ;;; ;; Make all Magit options available, even those that are disabled by ;; default because they're too obscure. (setq transient-default-level 7) ;;; with-editor ;;; (add-hook 'eshell-mode-hook 'with-editor-export-editor) (add-hook 'shell-mode-hook 'with-editor-export-editor) (add-hook 'term-exec-hook 'with-editor-export-editor)