diff --git a/init.el b/init.el index d6511c4..d581c86 100644 --- a/init.el +++ b/init.el @@ -22,13 +22,9 @@ 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 'complete 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) @@ -72,12 +68,15 @@ ;; 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))) +(setq backup-directory-alist '(("." . "~/.local/state/emacs/backups"))) (savehist-mode +1) +(use-package dired + :custom + (dired-auto-revert-buffer t) + (dired-dwim-target t)) + (use-package recentf :config (run-at-time nil (* 5 60) 'recentf-save-list) @@ -85,13 +84,18 @@ :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))) +(use-package auth-source + :custom + (auth-sources '("secrets:Login"))) + +(use-package auth-source-pass + :requires auth-source + :config + (auth-source-pass-enable)) ;; Make `describe-*' screens more helpful (use-package helpful + :defines helpful-mode-map :bind ((" " . helpful-command) (" " . helpful-callable) (" " . helpful-key) @@ -114,6 +118,7 @@ (prog-mode . flyspell-prog-mode)) :init (require 'ispell) + (require 'org) ; Fails without org-mode-map for some reason... :custom (flyspell-mode-line-string nil) (flyspell-use-meta-tab nil) @@ -130,18 +135,18 @@ :after (consult flyspell) :bind ( :map flyspell-mode-map ("C-c s ;" . consult-flyspell)) - :config - (setq consult-flyspell-always-check-buffer t)) + :custom + (consult-flyspell-always-check-buffer t)) (use-package ibuffer + :defines ibuffer-filter-groups :bind (("C-c b" . ibuffer))) (use-package ibuffer-project :after (ibuffer project) - :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)))))) + :functions ibuffer-project-generate-filter-groups + :hook + ((ibuffer . (lambda () (setq ibuffer-filter-groups (ibuffer-project-generate-filter-groups)))))) (use-package ace-window :bind ((" " . ace-window)) @@ -167,211 +172,9 @@ (use-package eshell :bind (("C-c t e" . eshell))) -(use-package eww - :defer t +(use-package xref :custom - (browse-url-browser-function 'browse-url-default-browser) - (browse-url-secondary-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 nil) - (corfu-quit-no-match 'separator) - (corfu-quit-at-boundary 'separator) - (corfu-preview-current 'insert) - (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 - :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) - ("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 org-mode-map - (" " . consult-org-heading) - (" " . consult-org-heading) - :map minibuffer-local-map - (" " . consult-history) - :map comint-mode-map - (" " . 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)) - -(use-package tempel - :custom - (tempel-trigger-prefix "<") - :bind (("M-+" . tempel-complete) - ("M-*" . tempel-insert)) - :init - (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)) - -(use-package license-templates - :defer t) - -(use-package gitignore-templates - :defer t) - -;; 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) - -;; 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-file () - "Open elfeed org source file." - (interactive) - (require 'elfeed) - (require 'elfeed-org) - (find-file (car rmh-elfeed-org-files))) -(keymap-global-set "C-c w f" #'my/open-feeds-file) - -(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) + (xref-show-definitions-function 'xref-show-definitions-completing-read)) (use-package calendar :bind (("C-c >" . calendar)) @@ -406,13 +209,16 @@ it will be tried first." (add-hook 'prog-mode-hook #'(lambda () (display-line-numbers-mode +1))) (use-package which-key + :functions which-key-mode :config (which-key-mode +1)) -(use-package page-break-lines - :config (global-page-break-lines-mode +1)) +(use-package elec-pair + :config + (electric-pair-mode +1)) -(electric-pair-mode +1) -(show-paren-mode +1) +(use-package paren + :config + (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/ @@ -434,7 +240,10 @@ it will be tried first." (load-theme 'base16-catppuccin-mocha t)) (use-package nerd-icons - :config (nerd-icons-set-font "Symbols Nerd Font Mono-12")) + :functions (nerd-icons-set-font + nerd-icons-octicon) + :config + (nerd-icons-set-font "Symbols Nerd Font Mono-12")) (use-package nerd-icons-dired :diminish @@ -448,17 +257,18 @@ it will be tried first." (use-package nerd-icons-completion :diminish + :functions nerd-icons-completion-mode :after nerd-icons :config (nerd-icons-completion-mode +1)) (use-package nerd-icons-corfu :diminish - :after (corfu nerd-icons) + :requires (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 + :functions (ligature-set-ligatures + global-ligature-mode) :config (ligature-set-ligatures '(text-mode prog-mode org-mode) @@ -483,28 +293,6 @@ it will be tried first." (setq mode-line-compact 'long) -(use-package doom-modeline - :disabled - :demand - :custom - (doom-modeline-checker-simple-format nil) - (doom-modeline-enable-word-count t) - (doom-modeline-env-version t) - (doom-modeline-github nil) - (doom-modeline-gnus t) - (doom-modeline-mu4e nil) ; Built-in implementation looks nicer - (doom-modeline-icon t) - (doom-modeline-irc t) - (doom-modeline-irc-buffers t) - (doom-modeline-lsp t) - (doom-modeline-project-detection 'project) - (doom-modeline-continuous-word-count-modes '(org-mode - markdown-mode - gfm-mode)) - :config - (set-face-attribute 'doom-modeline nil :weight 'normal) - (doom-modeline-mode +1)) - (line-number-mode -1) (column-number-mode -1) (size-indication-mode +1) @@ -518,6 +306,9 @@ it will be tried first." (display-battery-mode +1)) (use-package diff-hl + :functions (diff-hl-magit-pre-refresh + diff-hl-magit-post-refresh + global-diff-hl-mode) :init (add-hook 'magit-pre-refresh-hook #'diff-hl-magit-pre-refresh) (add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh) @@ -543,8 +334,14 @@ it will be tried first." :custom (ediff-window-setup-function #'ediff-setup-windows-plain)) -(setq emms-mode-line-icon-enabled-p nil) (use-package emms + :defines (emms-browser-mode-map + emms-playlist-mode-map) + :functions (emms-all + emms-default-players + emms-mpris-enable + emms-cache-enable + emms-show) :bind (("C-c e e" . emms-smart-browse) ("C-c e p" . emms-pause) ("C-c e s" . emms-stop) @@ -576,7 +373,7 @@ it will be tried first." (add-hook 'emms-player-started-hook #'emms-show)) (use-package org - :demand + :defines org-mode-map :hook ((org-mode . turn-on-auto-fill)) :custom (org-directory "~/Documents/org") @@ -701,6 +498,8 @@ it will be tried first." (use-package org-roam :after org + :defines org-roam-directory + :functions org-roam-db-autosync-mode :bind (("C-c r i" . org-roam-node-insert) ("C-c r f" . org-roam-node-find) ("C-c r n" . org-roam-capture) @@ -776,7 +575,7 @@ it will be tried first." (use-package org-noter :after (org doc-view citar) - :commands (org-noter) + :commands org-noter :custom (org-noter-always-create-frame nil) (org-noter-kill-frame-at-session-end nil) @@ -789,6 +588,16 @@ it will be tried first." (org-noter-prefer-root-as-file-level nil)) (use-package citar + :defines (citar-bibliography + citar-indicators) + :functions (citar-indicator-create + citar-has-files + citar-has-links + citar-has-notes + citar-is-cited) + :hook + (LaTeX-mode . citar-capf-setup) + (org-mode . citar-capf-setup) :custom (citar-bibliography (list @@ -799,9 +608,6 @@ it will be tried first." (citar-notes-paths (list (expand-file-name "citar/notes/" org-directory))) - :hook - (LaTeX-mode . citar-capf-setup) - (org-mode . citar-capf-setup) :config (require 'org) (require 'nerd-icons) @@ -852,8 +658,9 @@ it will be tried first." citar-indicator-cited-icons))) (use-package citar-embark - :after citar :diminish + :requires citar + :functions citar-embark-mode :init (require 'embark) :config @@ -862,6 +669,145 @@ it will be tried first." (setq org-latex-compiler "lualatex") (setq org-preview-latex-default-process 'dvisvgm) +(use-package vertico + :functions vertico-mode + :custom + (vertico-cycle t) + :config + (vertico-mode +1) + (require 'vertico-directory)) + +(use-package marginalia + :functions marginalia-mode + :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 + :defines corfu-map + :functions (global-corfu-mode + corfu-history-mode) + :bind ( :map corfu-map + ("M-SPC" . corfu-insert-separator) + ("RET" . nil) + ("TAB" . corfu-insert) + ([tab] . corfu-insert)) + :custom + (corfu-cycle t) + (corfu-auto nil) + (corfu-quit-no-match 'separator) + (corfu-quit-at-boundary 'separator) + (corfu-preview-current 'insert) + (corfu-preselect 'directory) + :config + (require 'corfu-popupinfo) + (require 'corfu-terminal) + (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-popupinfo + :requires corfu + :functions corfu-popupinfo-mode + :bind ( :map corfu-map + ("M-d" . corfu-popupinfo-toggle) + ("M-n" . corfu-popupinfo-scroll-up) + ("M-p" . corfu-popupinfo-scroll-down)) + :custom + (corfu-popupinfo-delay 0.3) + :config + (corfu-popupinfo-mode +1)) + +(use-package corfu-terminal + :requires corfu + :functions corfu-terminal-mode + :config + (corfu-terminal-mode +1)) + +(use-package cape + :after corfu + :custom + (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) + (cape-dabbrev-min-length (+ corfu-auto-prefix 1))) + +(use-package consult + :bind ((" " . consult-imenu) + (" " . consult-buffer) + (" " . consult-project-buffer) + ("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 org-mode-map + (" " . consult-org-heading) + (" " . consult-org-heading) + :map minibuffer-local-map + (" " . consult-history) + :map comint-mode-map + (" " . consult-history))) + +(use-package consult-eglot + :after (consult eglot) + :bind (("C-c s s" . consult-eglot-symbols))) + +(use-package embark + :functions embark-prefix-help-command + :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)) + +(use-package tempel + :defines tempel-path + :functions (tempel-expand + tempel-abbrev-mode + global-tempel-abbrev-mode) + :bind (("M-+" . tempel-complete) + ("M-*" . tempel-insert)) + :custom + (tempel-trigger-prefix "<") + :init + (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)) + (require 'tramp) (setq org-publish-project-alist `(("xenia.me.uk" @@ -892,6 +838,7 @@ it will be tried first." (use-package gud :defer t + :defines gdb-many-windows :config (setq gdb-many-windows t)) @@ -900,7 +847,9 @@ it will be tried first." (treesit-font-lock-level 3)) (use-package treesit-auto - :after (treesit) + :requires treesit + :functions (treesit-auto-add-to-auto-mode-alist + global-treesit-auto-mode) :config (treesit-auto-add-to-auto-mode-alist) (global-treesit-auto-mode +1)) @@ -951,18 +900,21 @@ it will be tried first." :flake ( :autoArchive t :nixpkgsInputName "nixpkgs"))))) - (with-eval-after-load 'cape - (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster) - (defun my/eglot-capf () - (setq-local completion-at-point-functions - (list (cape-capf-super - #'eglot-completion-at-point - #'tempel-expand - #'cape-file)))) - (add-hook 'eglot-managed-mode-hook #'my/eglot-capf))) + ;; (with-eval-after-load 'cape + ;; (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster) + ;; (defun my/eglot-capf () + ;; (setq-local completion-at-point-functions + ;; (list (cape-capf-super + ;; #'eglot-completion-at-point + ;; #'tempel-expand + ;; #'cape-file)))) + ;; (add-hook 'eglot-managed-mode-hook #'my/eglot-capf)) + ) (use-package apheleia :diminish + :defines (apheleia-formatters + apheleia-mode-alist) :bind (("C-c c f" . apheleia-format-buffer)) :hook (prog-mode) :custom (apheleia-remote-algorithm 'local) @@ -981,6 +933,7 @@ it will be tried first." (use-package flymake-popon :diminish + :functions flymake-popon-mode :requires flymake :hook (flymake-mode . (lambda () (flymake-popon-mode +1)))) @@ -991,12 +944,15 @@ it will be tried first." (use-package flymake-yamllint :if (executable-find "yamllint") + :functions flymake-yamllint-setup :requires flymake :hook (yaml-ts-mode . (lambda () (progn (flymake-mode +1) (flymake-yamllint-setup))))) (use-package project + :functions (project-forget-zombie-projects + project-remember-projects-under) :custom (project-switch-use-entire-map t) (project-switch-commands '((project-find-file "Find file") @@ -1008,7 +964,7 @@ it will be tried first." (defun my/project-find-common-projects () "Search and remember common project directories. -Calls `project-remember-project-under' for ~/Projects/" +Calls `project-remember-projects-under' for ~/Projects/" (interactive) (require 'project) (project-forget-zombie-projects) @@ -1049,6 +1005,7 @@ Calls `project-remember-project-under' for ~/Projects/" (use-package nix-mode :mode "\\.nix\\'" + :functions nix-prettify-global-mode :config (require 'nix) (require 'nix-flake) @@ -1088,6 +1045,8 @@ Calls `project-remember-project-under' for ~/Projects/" :defer t :custom (tramp-default-method "scpx") + (tramp-backup-directory-alist backup-directory-alist) + (tramp-auto-save-directory (cdr (assoc "." tramp-backup-directory-alist))) :config (add-to-list 'tramp-remote-path 'tramp-own-remote-path) (add-to-list 'tramp-remote-path "~/.local/bin/")) @@ -1141,311 +1100,61 @@ Calls `project-remember-project-under' for ~/Projects/" :after (markdown-mode) :hook (markdown-mode . conditionally-turn-on-pandoc)) -(setq sendmail-program (executable-find "msmtp") - send-mail-function #'sendmail-send-it - message-send-mail-function #'message-send-mail-with-sendmail - message-sendmail-f-is-evil t - message-sendmail-extra-arguments '("--read-envelope-from") - message-auto-save-directory nil - message-kill-buffer-on-exit t - mail-user-agent 'mu4e-user-agent - read-mail-command 'mu4e) - -(use-package mu4e - :bind - (("C-c m" . mu4e) - :map mu4e-view-mode-map - ("o n" . mu4e-org-store-and-capture)) +(use-package sendmail :custom - (mu4e-read-option-use-builtin nil) - (mu4e-completing-read-function #'completing-read) - (mu4e-split-view 'horizontal) - (mu4e-attachment-dir "~/Downloads") - (mu4e-get-mail-command "mbsync -a") - (mu4e-update-interval (* 5 60)) ; Every 5 minutes - (mu4e-headers-auto-update nil) - (mu4e-sent-messages-behavior 'sent) - (mu4e-change-filenames-when-moving t) - (mu4e-context-policy 'pick-first) - (mu4e-compose-context-policy 'ask) - (mu4e-compose-signature-auto-include nil) - (mu4e-search-full nil) - (mu4e-search-include-related t) - (mu4e-search-threads t) - (mu4e-search-skip-duplicates t) - (mu4e-maildir-shortcuts '((:maildir "/Proton/Inbox/" :key ?p) - (:maildir "/iCloud/Inbox/" :key ?i) - (:maildir "/Outlook/Inbox/" :key ?w))) - (mu4e-bookmarks '((:name "Inbox" :query "maildir:/inbox/" :key ?i :favorite t) - (:name "Today" :query "date:today..now AND maildir:/inbox/" :key ?t) - (:name "Flagged" :query "flag:flagged AND NOT flag:trashed AND NOT maildir:/spam/ AND NOT maildir:/junk/" :key ?f) - (:name "Unread" :query "flag:unread AND NOT flag:trashed AND NOT maildir:/archive/ AND NOT maildir:/spam/ AND NOT maildir:/junk/" :key ?u :hide-unread t) - (:name "Spam" :query "maildir:/spam/ OR maildir:/junk/" :key ?s :hide-unread t))) - (mu4e-headers-visible-lines 3) - (mu4e-headers-fields - '((:human-date . 8) - (:flags . 10) - (:from . 22) - (:subject))) - (mu4e-headers-visible-flags - '(draft - flagged - unread - passed - replied - trashed - attach - calendar - encrypted - signed - list - personal)) - :config - (setq mu4e-use-fancy-chars t) + (sendmail-program (executable-find "msmtp")) + (send-mail-function #'sendmail-send-it)) - ) - -(with-eval-after-load 'mu4e - (require 'mu4e-context) - (setq mu4e-contexts - (list - (make-mu4e-context - :name "Personal" - :match-func (lambda (msg) (when msg (string-prefix-p "/Proton" (mu4e-message-field msg :maildir)))) - :vars '( - (user-mail-address . "e.litherlandsmith@proton.me") - (mu4e-sent-folder . "/Proton/Sent") - (mu4e-trash-folder . "/Proton/Trash") - (mu4e-refile-folder . "/Proton/Archive") - (message-cite-style . message-cite-style-thunderbird) - )) - (make-mu4e-context - :name "Alternate" - :match-func (lambda (msg) (when msg (string-prefix-p "/iCloud" (mu4e-message-field msg :maildir)))) - :vars '( - (user-mail-address . "e.litherlandsmith@icloud.com") - (mu4e-sent-folder . "/iCloud/Sent Messages") - (mu4e-trash-folder . "/iCloud/Deleted Messages") - (mu4e-refile-folder . "/iCloud/Archive") - (message-cite-style . message-cite-style-thunderbird) - )) - (make-mu4e-context - :name "Work" - :match-func (lambda (msg) (when msg (string-prefix-p "/Outlook" (mu4e-message-field msg :maildir)))) - :vars '( - (user-mail-address . "evie.litherland-smith@ukaea.uk") - (mu4e-sent-folder . "/Outlook/Sent") - (mu4e-trash-folder . "/Outlook/Trash") - (mu4e-refile-folder . "/Outlook/Archive") - (message-cite-style . message-cite-style-outlook) - (message-signature . (concat "Evelyn Litherland-Smith (she/they)\n" - "Spectroscopy Diagnostic Physicist\n" - "Plasma Science and Fusion Operations\n" - "UK Atomic Energy Authority")) - ))))) - -(with-eval-after-load 'mu4e - (require 'mu4e-modeline) - (setq mu4e-modeline-all-read '("R:" . " ") - mu4e-modeline-all-clear '("C:" . " ") - mu4e-modeline-new-items '("N:" . " ") - mu4e-modeline-unread-items '("U:" . " ")) - (mu4e-modeline-mode +1)) - -(with-eval-after-load 'mu4e - (setq mu4e-search-full-label '("F" . " ") - mu4e-search-hide-label '("H" . " ") - mu4e-search-related-label '("R" . " ") - mu4e-search-skip-duplicates-label '("D" . " ") - mu4e-search-threaded-label'("T" . " ") - mu4e-headers-draft-mark '("D" . " ") - mu4e-headers-flagged-mark '("F" . " ") - mu4e-headers-unread-mark '("u" . " ") - mu4e-headers-passed-mark '("P" . " ") - mu4e-headers-replied-mark '("R" . " ") - mu4e-headers-trashed-mark '("T" . " ") - mu4e-headers-attach-mark '("a" . " ") - mu4e-headers-calendar-mark '("c" . " ") - mu4e-headers-encrypted-mark '("x" . " ") - mu4e-headers-signed-mark '("s" . " ") - mu4e-headers-list-mark '("l" . " ") - mu4e-headers-personal-mark '("p" . " ") - mu4e-headers-seen-mark '("S" . " ") - mu4e-headers-new-mark '("N" . " ") - mu4e-headers-from-or-to-prefix '(" " . "To ") - mu4e-headers-thread-root-prefix '("* " . "* ") - mu4e-headers-thread-duplicate-prefix '("= " . "= ") - mu4e-headers-thread-blank-prefix '(" " . " ") - mu4e-headers-thread-single-orphan-prefix '("─>" . "─>") - mu4e-headers-thread-orphan-prefix '("┬>" . "┬>") - mu4e-headers-thread-connection-prefix '("│ " . "│ ") - mu4e-headers-thread-first-child-prefix '("├>" . "├>") - mu4e-headers-thread-child-prefix '("├>" . "├>") - mu4e-headers-thread-last-child-prefix '("└>" . "╰>"))) - -(with-eval-after-load 'mu4e - (setq mu4e-marks '((refile :char - ("r" . " ") - :prompt "refile" :dyn-target - (lambda - (target msg) - (mu4e-get-refile-folder msg)) - :action - (lambda - (docid msg target) - (mu4e--server-move docid - (mu4e--mark-check-target target) - "-N"))) - (delete :char - ("D" . " ") - :prompt "Delete" :show-target - (lambda - (target) - "delete") - :action - (lambda - (docid msg target) - (mu4e--server-remove docid))) - (flag :char - ("+" . " ") - :prompt "+flag" :show-target - (lambda - (target) - "flag") - :action - (lambda - (docid msg target) - (mu4e--server-move docid nil "+F-u-N"))) - (move :char - ("m" . " ") - :prompt "move" :ask-target mu4e--mark-get-move-target :action - (lambda - (docid msg target) - (mu4e--server-move docid - (mu4e--mark-check-target target) - "-N"))) - (read :char - ("!" . " ") - :prompt "!read" :show-target - (lambda - (target) - "read") - :action - (lambda - (docid msg target) - (mu4e--server-move docid nil "+S-u-N"))) - (trash :char - ("d" . " ") - :prompt "dtrash" :dyn-target - (lambda - (target msg) - (mu4e-get-trash-folder msg)) - :action - (lambda - (docid msg target) - (mu4e--server-move docid - (mu4e--mark-check-target target) - "+T-N"))) - (unflag :char - ("-" . " ") - :prompt "-unflag" :show-target - (lambda - (target) - "unflag") - :action - (lambda - (docid msg target) - (mu4e--server-move docid nil "-F-N"))) - (untrash :char - ("=" . " ") - :prompt "=untrash" :show-target - (lambda - (target) - "untrash") - :action - (lambda - (docid msg target) - (mu4e--server-move docid nil "-T"))) - (unread :char - ("?" . " ") - :prompt "?unread" :show-target - (lambda - (target) - "unread") - :action - (lambda - (docid msg target) - (mu4e--server-move docid nil "-S+u-N"))) - (unmark :char " " :prompt "unmark" :action - (mu4e-error "No action for unmarking")) - (action :char - ("a" . " ") - :prompt "action" :ask-target - (lambda nil - (mu4e-read-option "Action: " mu4e-headers-actions)) - :action - (lambda - (docid msg actionfunc) - (save-excursion - (when - (mu4e~headers-goto-docid docid) - (mu4e-headers-action actionfunc))))) - (something :char - ("*" . " ") - :prompt "*something" :action - (mu4e-error "No action for deferred mark"))))) - -(with-eval-after-load 'mu4e - (require 'mu4e-notification) - (setq mu4e-notification-support t)) - -(use-package gnus-icalendar - :after mu4e +(use-package message :custom - (gnus-icalendar-org-capture-file (expand-file-name "agenda/invited.org" org-directory)) - (gnus-icalendar-org-capture-headline '("Email" "Inbox")) - :config - (require 'org-agenda) - (require 'org-capture) - (gnus-icalendar-org-setup)) + (message-send-mail-function #'message-send-mail-with-sendmail) + (message-sendmail-f-is-evil t) + (message-sendmail-extra-arguments '("--read-envelope-from")) + (message-auto-save-directory nil) + (message-kill-buffer-on-exit t)) -(use-package mu4e-icalendar - :after mu4e - :custom - (mu4e-icalendar-trash-after-reply nil) - :config - (require 'gnus-icalendar) - (mu4e-icalendar-setup) - (gnus-icalendar-org-setup)) +(load-file (locate-user-emacs-file "mu4e-custom.el")) (use-package erc + :commands erc-compute-nick :custom (erc-nick (user-login-name)) - (erc-user-full-name (user-full-name))) + (erc-user-full-name (user-full-name)) + :config + (when (require 'password-store nil :noerror) + (defun my/libera-chat-connect () + "Connect to irc.libera.chat directly." + (interactive) + (require 'erc) + (require 'password-store) + (erc-tls + :server "irc.libera.chat" + :password (password-store-get 'irc.libera.chat))) + (defun my/znc-connect () + "Connect to my ZNC IRC bouncer." + (interactive) + (require 'erc) + (require 'password-store) + (erc-tls + :server "xenia.me.uk" + :port 6697 + :nick (concat (erc-compute-nick) "/liberachat") + :password (password-store-get 'znc))))) -(defun my/libera-chat-connect () - "Connect to irc.libera.chat directly." - (interactive) - (require 'erc) - (require 'password-store) - (erc-tls - :server "irc.libera.chat" - :password (password-store-get 'irc.libera.chat))) - -(defun my/znc-connect () - "Connect to my ZNC IRC bouncer." - (interactive) - (require 'erc) - (require 'password-store) - (erc-tls - :server "xenia.me.uk" - :port 6697 - :nick (concat (erc-compute-nick) "/liberachat") - :password (password-store-get 'znc))) +(use-package eww + :defer t + :custom + (browse-url-browser-function 'browse-url-default-browser) + (browse-url-secondary-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 elfeed :bind (("C-c f" . elfeed)) - :hook (elfeed-search-mode . elfeed-update) + :hook ((elfeed-search-mode . elfeed-update) + (elfeed-show-mode . (lambda () (visual-line-mode +1)))) :custom (elfeed-search-filter "@2-months-ago +unread") :config @@ -1459,6 +1168,8 @@ Calls `project-remember-project-under' for ~/Projects/" (use-package elfeed-org :after (elfeed elfeed-db org) + :defines rmh-elfeed-org-files + :functions elfeed-org :custom (rmh-elfeed-org-files (list (expand-file-name "feeds.org" elfeed-db-directory))) @@ -1467,6 +1178,9 @@ Calls `project-remember-project-under' for ~/Projects/" (use-package elfeed-tube :after elfeed + :defines (elfeed-show-mode-map + elfeed-search-mode-map) + :functions elfeed-tube-setup :bind ( :map elfeed-show-mode-map ("F" . elfeed-tube-fetch) ([remap save-buffer] . elfeed-tube-save) @@ -1480,10 +1194,7 @@ Calls `project-remember-project-under' for ~/Projects/" (elfeed-tube-setup)) (use-package password-store - :defer t) - -(use-package password-store-otp - :defer t) + :functions password-store-get) (use-package pass :defer t @@ -1492,5 +1203,58 @@ Calls `project-remember-project-under' for ~/Projects/" (pass-show-keybindings nil) (pass-username-field "login")) +;; 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) + +;; 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-file () + "Open elfeed org source file." + (interactive) + (require 'elfeed) + (require 'elfeed-org) + (find-file (car rmh-elfeed-org-files))) +(keymap-global-set "C-c w f" #'my/open-feeds-file) + +(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) + (provide 'init) ;;; init.el ends here diff --git a/mu4e-custom.el b/mu4e-custom.el new file mode 100644 index 0000000..6f678b5 --- /dev/null +++ b/mu4e-custom.el @@ -0,0 +1,282 @@ +;;; mu4e-custom.el -- mu4e -*- lexical-binding: t -*- +;;; Commentary: +;;; Code: +(setq sendmail-program (executable-find "msmtp") + send-mail-function #'sendmail-send-it + message-send-mail-function #'message-send-mail-with-sendmail + message-sendmail-f-is-evil t + message-sendmail-extra-arguments '("--read-envelope-from") + message-auto-save-directory nil + message-kill-buffer-on-exit t + mail-user-agent 'mu4e-user-agent + read-mail-command 'mu4e) + +(use-package mu4e + :bind + (("C-c m" . mu4e) + :map mu4e-view-mode-map + ("o n" . mu4e-org-store-and-capture)) + :custom + (mu4e-read-option-use-builtin nil) + (mu4e-completing-read-function #'completing-read) + (mu4e-split-view 'horizontal) + (mu4e-attachment-dir "~/Downloads") + (mu4e-get-mail-command "mbsync -a") + (mu4e-update-interval (* 5 60)) ; Every 5 minutes + (mu4e-headers-auto-update nil) + (mu4e-sent-messages-behavior 'sent) + (mu4e-change-filenames-when-moving t) + (mu4e-context-policy 'pick-first) + (mu4e-compose-context-policy 'ask) + (mu4e-compose-signature-auto-include nil) + (mu4e-search-full nil) + (mu4e-search-include-related t) + (mu4e-search-threads t) + (mu4e-search-skip-duplicates t) + (mu4e-maildir-shortcuts '((:maildir "/Proton/Inbox/" :key ?p) + (:maildir "/iCloud/Inbox/" :key ?i) + (:maildir "/Outlook/Inbox/" :key ?w))) + (mu4e-bookmarks '((:name "Inbox" :query "maildir:/inbox/" :key ?i :favorite t) + (:name "Today" :query "date:today..now AND maildir:/inbox/" :key ?t) + (:name "Flagged" :query "flag:flagged AND NOT flag:trashed AND NOT maildir:/spam/ AND NOT maildir:/junk/" :key ?f) + (:name "Unread" :query "flag:unread AND NOT flag:trashed AND NOT maildir:/archive/ AND NOT maildir:/spam/ AND NOT maildir:/junk/" :key ?u :hide-unread t) + (:name "Spam" :query "maildir:/spam/ OR maildir:/junk/" :key ?s :hide-unread t))) + (mu4e-headers-visible-lines 3) + (mu4e-headers-fields + '((:human-date . 8) + (:flags . 10) + (:from . 22) + (:subject))) + (mu4e-headers-visible-flags + '(draft + flagged + unread + passed + replied + trashed + attach + calendar + encrypted + signed + list + personal)) + :config + (setq mu4e-use-fancy-chars t) + + ) + +(with-eval-after-load 'mu4e + (require 'mu4e-context) + (setq mu4e-contexts + (list + (make-mu4e-context + :name "Personal" + :match-func (lambda (msg) (when msg (string-prefix-p "/Proton" (mu4e-message-field msg :maildir)))) + :vars '( + (user-mail-address . "e.litherlandsmith@proton.me") + (mu4e-sent-folder . "/Proton/Sent") + (mu4e-trash-folder . "/Proton/Trash") + (mu4e-refile-folder . "/Proton/Archive") + (message-cite-style . message-cite-style-thunderbird) + )) + (make-mu4e-context + :name "Alternate" + :match-func (lambda (msg) (when msg (string-prefix-p "/iCloud" (mu4e-message-field msg :maildir)))) + :vars '( + (user-mail-address . "e.litherlandsmith@icloud.com") + (mu4e-sent-folder . "/iCloud/Sent Messages") + (mu4e-trash-folder . "/iCloud/Deleted Messages") + (mu4e-refile-folder . "/iCloud/Archive") + (message-cite-style . message-cite-style-thunderbird) + )) + (make-mu4e-context + :name "Work" + :match-func (lambda (msg) (when msg (string-prefix-p "/Outlook" (mu4e-message-field msg :maildir)))) + :vars '( + (user-mail-address . "evie.litherland-smith@ukaea.uk") + (mu4e-sent-folder . "/Outlook/Sent") + (mu4e-trash-folder . "/Outlook/Trash") + (mu4e-refile-folder . "/Outlook/Archive") + (message-cite-style . message-cite-style-outlook) + (message-signature . (concat "Evelyn Litherland-Smith (she/they)\n" + "Spectroscopy Diagnostic Physicist\n" + "Plasma Science and Fusion Operations\n" + "UK Atomic Energy Authority")) + ))))) + +(with-eval-after-load 'mu4e + (require 'mu4e-modeline) + (setq mu4e-modeline-all-read '("R:" . " ") + mu4e-modeline-all-clear '("C:" . " ") + mu4e-modeline-new-items '("N:" . " ") + mu4e-modeline-unread-items '("U:" . " ")) + (mu4e-modeline-mode +1)) + +(with-eval-after-load 'mu4e + (setq mu4e-search-full-label '("F" . " ") + mu4e-search-hide-label '("H" . " ") + mu4e-search-related-label '("R" . " ") + mu4e-search-skip-duplicates-label '("D" . " ") + mu4e-search-threaded-label'("T" . " ") + mu4e-headers-draft-mark '("D" . " ") + mu4e-headers-flagged-mark '("F" . " ") + mu4e-headers-unread-mark '("u" . " ") + mu4e-headers-passed-mark '("P" . " ") + mu4e-headers-replied-mark '("R" . " ") + mu4e-headers-trashed-mark '("T" . " ") + mu4e-headers-attach-mark '("a" . " ") + mu4e-headers-calendar-mark '("c" . " ") + mu4e-headers-encrypted-mark '("x" . " ") + mu4e-headers-signed-mark '("s" . " ") + mu4e-headers-list-mark '("l" . " ") + mu4e-headers-personal-mark '("p" . " ") + mu4e-headers-seen-mark '("S" . " ") + mu4e-headers-new-mark '("N" . " ") + mu4e-headers-from-or-to-prefix '(" " . "To ") + mu4e-headers-thread-root-prefix '("* " . "* ") + mu4e-headers-thread-duplicate-prefix '("= " . "= ") + mu4e-headers-thread-blank-prefix '(" " . " ") + mu4e-headers-thread-single-orphan-prefix '("─>" . "─>") + mu4e-headers-thread-orphan-prefix '("┬>" . "┬>") + mu4e-headers-thread-connection-prefix '("│ " . "│ ") + mu4e-headers-thread-first-child-prefix '("├>" . "├>") + mu4e-headers-thread-child-prefix '("├>" . "├>") + mu4e-headers-thread-last-child-prefix '("└>" . "╰>"))) + +(with-eval-after-load 'mu4e + (setq mu4e-marks '((refile :char + ("r" . " ") + :prompt "refile" :dyn-target + (lambda + (target msg) + (mu4e-get-refile-folder msg)) + :action + (lambda + (docid msg target) + (mu4e--server-move docid + (mu4e--mark-check-target target) + "-N"))) + (delete :char + ("D" . " ") + :prompt "Delete" :show-target + (lambda + (target) + "delete") + :action + (lambda + (docid msg target) + (mu4e--server-remove docid))) + (flag :char + ("+" . " ") + :prompt "+flag" :show-target + (lambda + (target) + "flag") + :action + (lambda + (docid msg target) + (mu4e--server-move docid nil "+F-u-N"))) + (move :char + ("m" . " ") + :prompt "move" :ask-target mu4e--mark-get-move-target :action + (lambda + (docid msg target) + (mu4e--server-move docid + (mu4e--mark-check-target target) + "-N"))) + (read :char + ("!" . " ") + :prompt "!read" :show-target + (lambda + (target) + "read") + :action + (lambda + (docid msg target) + (mu4e--server-move docid nil "+S-u-N"))) + (trash :char + ("d" . " ") + :prompt "dtrash" :dyn-target + (lambda + (target msg) + (mu4e-get-trash-folder msg)) + :action + (lambda + (docid msg target) + (mu4e--server-move docid + (mu4e--mark-check-target target) + "+T-N"))) + (unflag :char + ("-" . " ") + :prompt "-unflag" :show-target + (lambda + (target) + "unflag") + :action + (lambda + (docid msg target) + (mu4e--server-move docid nil "-F-N"))) + (untrash :char + ("=" . " ") + :prompt "=untrash" :show-target + (lambda + (target) + "untrash") + :action + (lambda + (docid msg target) + (mu4e--server-move docid nil "-T"))) + (unread :char + ("?" . " ") + :prompt "?unread" :show-target + (lambda + (target) + "unread") + :action + (lambda + (docid msg target) + (mu4e--server-move docid nil "-S+u-N"))) + (unmark :char " " :prompt "unmark" :action + (mu4e-error "No action for unmarking")) + (action :char + ("a" . " ") + :prompt "action" :ask-target + (lambda nil + (mu4e-read-option "Action: " mu4e-headers-actions)) + :action + (lambda + (docid msg actionfunc) + (save-excursion + (when + (mu4e~headers-goto-docid docid) + (mu4e-headers-action actionfunc))))) + (something :char + ("*" . " ") + :prompt "*something" :action + (mu4e-error "No action for deferred mark"))))) + +(with-eval-after-load 'mu4e + (require 'mu4e-notification) + (setq mu4e-notification-support t)) + +(use-package gnus-icalendar + :after mu4e + :custom + (gnus-icalendar-org-capture-file (expand-file-name "agenda/invited.org" org-directory)) + (gnus-icalendar-org-capture-headline '("Email" "Inbox")) + :config + (require 'org-agenda) + (require 'org-capture) + (gnus-icalendar-org-setup)) + +(use-package mu4e-icalendar + :after mu4e + :custom + (mu4e-icalendar-trash-after-reply nil) + :config + (require 'gnus-icalendar) + (mu4e-icalendar-setup) + (gnus-icalendar-org-setup)) + +(provide 'mu4e-custom) +;;; mu4e-custom.el ends here