;;; init.el -- My emacs config -*- lexical-binding: t -*- ;;; Commentary: ;; Moving my Emacs config from separate directory ;; To install packages on non-NixOS systems run `install.el' ;;; Code: ;; Stop popups for warning messages, keep in log buffer (setopt warning-minimum-level :error) ;; Configure packages archives with priority (load-file (locate-user-emacs-file "package-config.el")) (setq custom-file (locate-user-emacs-file "custom.el")) (when (and custom-file (file-exists-p custom-file)) (load custom-file nil 'nomessage)) (setq user-full-name "Evie Litherland-Smith" user-mail-address "evie@xenia.me.uk" use-short-answers t load-prefer-newer t even-window-sizes t global-auto-revert-non-file-buffers t dired-auto-revert-buffer t dired-dwim-target t tab-always-indent t completion-cycle-threshold nil completions-detailed t xref-show-definitions-function #'xref-show-definitions-completing-read kill-do-not-save-duplicates t) (setopt scroll-conservatively 101) (use-package pixel-scroll :init (pixel-scroll-precision-mode +1)) (use-package mwheel :custom (mouse-wheel-scroll-amount '(1 ((shift) . 1))) (mouse-wheel-progressive-speed nil) (mouse-wheel-follow-mouse t)) ;; Bind normal forward/back buttons on mouse to next/previous buffer respectively (keymap-global-set "" #'previous-buffer) (keymap-global-set "" #'next-buffer) ;; Quick bind for calling `git-sync-all' (defun my/git-sync-all () "Run shell command `git-sync-all' asynchronously." (interactive) (async-shell-command "git-sync-all" "*git-sync-all*" "*git-sync-errors*") (when (require 'org-journal nil :noerror) (org-journal-invalidate-cache))) (keymap-global-set "C-c g s" #'my/git-sync-all) (set-default-coding-systems 'utf-8) (set-terminal-coding-system 'utf-8) (set-keyboard-coding-system 'utf-8) (global-auto-revert-mode +1) (delete-selection-mode +1) (setopt ;; No tabs indent-tabs-mode nil ;; Only display async output buffer when there's something to show async-shell-command-display-buffer nil ;; Scroll compilation buffer output compilation-scroll-output t) ;; Make shebang (#!) file executable when saved (add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p) (setq backup-directory-alist '(("." . "~/.local/state/emacs/backups")) tramp-backup-directory-alist backup-directory-alist tramp-auto-save-directory (cdr (assoc "." tramp-backup-directory-alist))) (savehist-mode +1) (use-package recentf :config (run-at-time nil (* 5 60) 'recentf-save-list) (recentf-mode +1) :custom (recentf-max-saved-items 2048)) (when (require 'auth-source nil :noerror) (setq auth-sources '("secrets:Login")) (when (require 'auth-source-pass nil :noerror) (auth-source-pass-enable))) ;; Make `describe-*' screens more helpful (use-package helpful :bind ((" " . helpful-command) (" " . helpful-callable) (" " . helpful-key) (" " . helpful-symbol) (" " . helpful-variable) ("C-h F" . helpful-function) :map helpful-mode-map (" " . helpful-update))) ;; Bind extra `describe-*' commands (keymap-global-set "C-h K" #'describe-keymap) ;; turn on spell checking, if available. (use-package ispell :custom (ispell-dictionary "en_GB")) (use-package flyspell :hook ((text-mode . flyspell-mode) (prog-mode . flyspell-prog-mode)) :init (require 'ispell) :custom (flyspell-mode-line-string nil) (flyspell-use-meta-tab nil) :config (require 'flyspell-correct) (require 'consult-flyspell)) (use-package flyspell-correct :after flyspell :bind ( :map flyspell-mode-map ("C-;" . flyspell-correct-wrapper))) (use-package consult-flyspell :after (consult flyspell) :bind ( :map flyspell-mode-map ("C-c s ;" . consult-flyspell)) :config (setq consult-flyspell-always-check-buffer t)) (use-package ibuffer :bind (("C-c b" . ibuffer))) (use-package ibuffer-project :after ibuffer :hook ((ibuffer . (lambda () (setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups)) (unless (eq ibuffer-sorting-mode 'project-file-relative) (ibuffer-do-sort-by-project-file-relative)))))) (use-package link-hint :bind (("C-c l o" . link-hint-open-link) ("C-c l c" . link-hint-copy-link) ("C-c l C-o" . link-hint-open-all-link) ("C-c l C-c" . link-hint-copy-all-link))) (use-package avy :bind (("C-c j j" . avy-goto-char-2) ("C-c j w" . avy-goto-word-0) ("C-c j c" . avy-goto-char) ("C-c j l" . avy-goto-line))) (use-package which-func :init (which-function-mode)) (use-package shell :bind (("C-c t s" . shell))) (use-package eshell :bind (("C-c t e" . eshell))) (use-package shr :config (require 'catppuccin-theme) (set-face-attribute 'shr-text nil :foreground (catppuccin-get-color 'text))) (use-package eww :defer t :custom (browse-url-browser-function 'browse-url-default-browser) (browse-url-new-window-flag t) (eww-default-download-directory "~/Downloads/") (eww-auto-rename-buffer 'title) (eww-browse-url-new-window-is-tab nil)) (use-package vertico :custom (vertico-cycle t) :init (vertico-mode) :config (require 'vertico-directory)) (use-package marginalia :custom (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil)) :config (marginalia-mode +1)) (use-package orderless :custom (completion-styles '(orderless basic)) (completion-category-defaults nil) (completion-category-overrides '((file (styles . (partial-completion))) (eglot (styles . (styles orderless))) (eglot-capf (styles . (styles orderless)))))) (use-package corfu :demand :custom (corfu-cycle t) (corfu-auto t) (corfu-auto-delay 0.2) (corfu-auto-prefix 2) (corfu-quit-no-match 'separator) (corfu-quit-at-boundary 'separator) (corfu-preview-current nil) (corfu-preselect 'directory) :bind ( :map corfu-map ("M-SPC" . corfu-insert-separator) ("RET" . nil) ("TAB" . corfu-insert) ([tab] . corfu-insert)) :init (global-corfu-mode +1) (corfu-history-mode +1) :config (when (require 'corfu-popupinfo nil :noerror) (setq corfu-popupinfo-delay 0.3) (corfu-popupinfo-mode +1) (keymap-set corfu-map "M-p" #'corfu-popupinfo-scroll-down) (keymap-set corfu-map "M-n" #'corfu-popupinfo-scroll-up) (keymap-set corfu-map "M-d" #'corfu-popupinfo-toggle)) (defun corfu-enable-always-in-minibuffer () "Enable Corfu in the minibuffer if Vertico is not active." (unless (or (bound-and-true-p vertico--input) (eq (current-local-map) read-passwd-map)) (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup corfu-auto nil ;; Enable/disable auto completion corfu-popupinfo-delay nil) (corfu-mode +1))) (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1) (defun my/local-corfu-no-auto () (setq-local corfu-auto nil)) (with-eval-after-load 'eshell (add-hook 'eshell-mode-hook 'my/local-corfu-no-auto)) (with-eval-after-load 'shell (add-hook 'shell-mode-hook 'my/local-corfu-no-auto)) (with-eval-after-load 'gud (add-hook 'gud-mode-hook 'my/local-corfu-no-auto))) (use-package corfu-terminal :after corfu :demand :config (corfu-terminal-mode +1)) (use-package cape :demand :init (add-to-list 'completion-at-point-functions #'cape-emoji) (add-to-list 'completion-at-point-functions #'cape-file) (add-to-list 'completion-at-point-functions #'cape-dabbrev) :custom (cape-dabbrev-min-length (+ corfu-auto-prefix 1))) (use-package consult :bind ((" " . consult-imenu) (" " . consult-buffer) (" " . consult-project-buffer) (" " . consult-org-heading) ("C-c s l" . consult-line) ("C-c s o" . consult-outline) ("C-c s f" . consult-fd) ("C-c s g" . consult-ripgrep) ("C-c s e" . consult-flymake) ("C-c s i" . consult-info) :map minibuffer-local-map ("C-r" . consult-history)) :config (setq completion-in-region-function #'consult-completion-in-region)) (use-package consult-eglot :after (consult eglot) :bind (("C-c s s" . consult-eglot-symbols))) (use-package embark :bind ((" " . embark-bindings) ("C-." . embark-act)) :config (setq prefix-help-command #'embark-prefix-help-command)) (use-package embark-consult :after (embark consult) :hook (embark-collect-mode . consult-preview-at-point-mode)) ;; Configure Tempel (use-package tempel ;; Require trigger prefix before template name when completing. ;; :custom ;; (tempel-trigger-prefix "<") :bind (("M-+" . tempel-complete) ;; Alternative tempel-expand ("M-*" . tempel-insert)) :init ;; Setup completion at point (defun tempel-setup-capf () ;; Add the Tempel Capf to `completion-at-point-functions'. ;; `tempel-expand' only triggers on exact matches. Alternatively use ;; `tempel-complete' if you want to see all matches, but then you ;; should also configure `tempel-trigger-prefix', such that Tempel ;; does not trigger too often when you don't expect it. NOTE: We add ;; `tempel-expand' *before* the main programming mode Capf, such ;; that it will be tried first. (setq-local completion-at-point-functions (cons #'tempel-expand completion-at-point-functions))) (add-hook 'conf-mode-hook 'tempel-setup-capf) (add-hook 'prog-mode-hook 'tempel-setup-capf) (add-hook 'text-mode-hook 'tempel-setup-capf) ;; Optionally make the Tempel templates available to Abbrev, ;; either locally or globally. `expand-abbrev' is bound to C-x '. (add-hook 'prog-mode-hook #'tempel-abbrev-mode) (global-tempel-abbrev-mode)) ;; Scratch buffer shortcut (keymap-global-set "C-c w x" #'scratch-buffer) ;; Config file shortcut (defun my/open-init-file () "Open Emacs init file." (interactive) (find-file (locate-user-emacs-file "init.el"))) (keymap-global-set "C-c w e" #'my/open-init-file) ;; NixOS flake shortcut (defun my/open-nixos-flake () "Open NixOS system config flake." (interactive) (let ((flake "/etc/nixos/flake.nix")) (if (file-exists-p flake) (find-file flake) (warn (concat flake " not found"))))) (keymap-global-set "C-c w n" #'my/open-nixos-flake) ;; Tempel template file shortcut (defun my/open-template-file () "Open `tempel' template file." (interactive) (require 'tempel) (find-file tempel-path)) (keymap-global-set "C-c w t" #'my/open-template-file) ;; Org directory shortcut (defun my/open-org-directory () "Open base `org-mode' directory in Dired." (interactive) (require 'org) (find-file org-directory)) (keymap-global-set "C-c w o" #'my/open-org-directory) (defun my/open-global-bibliography () "Open `org-cite-global-bibliography'." (interactive) (require 'org) (find-file (car org-cite-global-bibliography))) (keymap-global-set "C-c w b" #'my/open-global-bibliography) ;; Elfeed feeds directory shortcut (defun my/open-feeds-directory () "Open directory containing elfeed sources." (interactive) (require 'org) (require 'elfeed) (require 'elfeed-org) (find-file (file-name-directory (car rmh-elfeed-org-files)))) (keymap-global-set "C-c w f" #'my/open-feeds-directory) (defun my/open-documents-directory () "Open Documents directory." (interactive) (find-file "~/Documents/")) (defun my/open-downloads-directory () "Open Downloads directory." (interactive) (find-file "~/Downloads/")) (keymap-global-set "C-c w d" #'my/open-documents-directory) (keymap-global-set "C-c w C-d" #'my/open-downloads-directory) (defun my/open-projects-directory () "Open Projects directory. Try `magit-clone-default-directory' if available, fall back to ~/Projects otherwise." (interactive) (require 'magit) (find-file (if magit-clone-default-directory magit-clone-default-directory "~/Projects/"))) (keymap-global-set "C-c w p" #'my/open-projects-directory) (use-package calendar :bind (("C-c ." . calendar)) :custom (calendar-date-style 'iso) (calendar-mark-holidays-flag t) (calendar-mark-diary-entries-flag nil) (calendar-view-holidays-initially-flag nil) (calendar-view-diary-initially-flag nil)) (use-package appt :custom (appt-display-diary nil) (appt-display-format 'echo) (appt-display-interval 5) (appt-message-warning-time 15) :init (appt-activate +1)) (setq inhibit-splash-screen t use-dialog-box nil minibuffer-follows-selected-frame nil truncate-lines nil truncate-partial-width-windows nil) (global-prettify-symbols-mode +1) (global-display-line-numbers-mode -1) (tool-bar-mode -1) (add-hook 'prog-mode-hook #'(lambda () (display-line-numbers-mode +1))) (use-package which-key :config (which-key-mode +1)) (use-package page-break-lines :config (global-page-break-lines-mode +1)) (electric-pair-mode +1) (show-paren-mode +1) ;; add visual pulse when changing focus, like beacon but built-in ;; from from https://karthinks.com/software/batteries-included-with-emacs/ (defun pulse-line (&rest _) "Pulse the current line." (pulse-momentary-highlight-one-line (point))) (dolist (command '(scroll-up-command scroll-down-command recenter-top-bottom other-window)) (advice-add command :after #'pulse-line)) (use-package catppuccin-theme :demand :hook (server-after-make-frame . catppuccin-reload) :custom (catppuccin-flavour 'mocha) (catppuccin-italic-blockquotes t) (catppuccin-italic-comments t) (catppuccin-italic-variables nil) :config (load-theme 'catppuccin t)) (use-package nerd-icons :config (nerd-icons-set-font "Symbols Nerd Font Mono-12")) (use-package nerd-icons-dired :after nerd-icons :hook (dired-mode)) (use-package nerd-icons-ibuffer :after nerd-icons :hook (ibuffer-mode)) (use-package nerd-icons-completion :after nerd-icons :config (nerd-icons-completion-mode +1)) (use-package nerd-icons-corfu :after (corfu nerd-icons) :config (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)) (keymap-global-set "C-c i n" #'nerd-icons-insert) (use-package ligature :config (ligature-set-ligatures '(text-mode prog-mode org-mode) '("-<<" "-<" "-<-" "<--" "<---" "<<-" "<-" "->" "->>" "-->" "--->" "->-" ">-" ">>-" "=<<" "=<" "=<=" "<==" "<===" "<<=" "<=" "=>" "=>>" "==>" "===>" "=>=" ">=" ">>=" "<->" "<-->" "<--->" "<---->" "<=>" "<==>" "<===>" "<====>" "::" ":::" "__" "<~~" "" "/>" "~~>" "==" "!=" "/=" "~=" "<>" "===" "!==" "!===" "=/=" "=!=" "<:" ":=" "*=" "*+" "<*" "<*>" "*>" "<|" "<|>" "|>" "<." "<.>" ".>" "+*" "=*" "=:" ":>" "(*" "*)" "/*" "*/" "[|" "|]" "{|" "|}" "++" "+++" "\\/" "/\\" "|-" "-|" "