doom/config.el

497 lines
19 KiB
EmacsLisp

;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
;; Place your private configuration here! Remember, you do not need to run 'doom
;; sync' after modifying this file!
;; Some functionality uses this to identify you, e.g. GPG configuration, email
;; clients, file templates and snippets. It is optional.
;; (setq user-full-name "John Doe"
;; user-mail-address "john@doe.com")
;; Doom exposes five (optional) variables for controlling fonts in Doom:
;;
;; - `doom-font' -- the primary font to use
;; - `doom-variable-pitch-font' -- a non-monospace font (where applicable)
;; - `doom-big-font' -- used for `doom-big-font-mode'; use this for
;; presentations or streaming.
;; - `doom-unicode-font' -- for unicode glyphs
;; - `doom-serif-font' -- for the `fixed-pitch-serif' face
;;
;; See 'C-h v doom-font' for documentation and more examples of what they
;; accept. For example:
;;
;;(setq doom-font (font-spec :family "Fira Code" :size 12 :weight 'semi-light)
;; doom-variable-pitch-font (font-spec :family "Fira Sans" :size 13))
;;
;; If you or Emacs can't find your font, use 'M-x describe-font' to look them
;; up, `M-x eval-region' to execute elisp code, and 'M-x doom/reload-font' to
;; refresh your font settings. If Emacs still can't find your font, it likely
;; wasn't installed correctly. Font issues are rarely Doom issues!
;; There are two ways to load a theme. Both assume the theme is installed and
;; available. You can either set `doom-theme' or manually load a theme with the
;; `load-theme' function. This is the default:
(setq doom-theme 'doom-one)
(custom-theme-set-faces! nil
'((font-lock-constant-face
font-lock-function-name-face
font-lock-keyword-face
font-lock-type-face)
:weight bold)
'((show-paren-match
whitespace-line)
:background nil)
`((line-number-current-line)
:inherit ,(remq 'hl-line (internal-get-lisp-face-attribute
'line-number-current-line
:inherit))))
(after! avy
(let ((avy-lead-face-fg (internal-get-lisp-face-attribute 'avy-lead-face
:background))
(avy-lead-face-0-fg (internal-get-lisp-face-attribute 'avy-lead-face-0
:background))
(avy-lead-face-1-fg (internal-get-lisp-face-attribute 'avy-lead-face-1
:background))
(avy-lead-face-2-fg (internal-get-lisp-face-attribute 'avy-lead-face-2
:background)))
(custom-theme-set-faces! 'doom-one
`(avy-lead-face
:foreground ,avy-lead-face-fg
:background nil
:weight bold)
`(avy-lead-face-0
:foreground ,avy-lead-face-0-fg
:inherit avy-lead-face)
`(avy-lead-face-1
:foreground ,avy-lead-face-1-fg
:inherit avy-lead-face)
`(avy-lead-face-2
:foreground ,avy-lead-face-2-fg
:inherit avy-lead-face))))
(after! symbol-overlay
(custom-theme-set-faces! nil
'(symbol-overlay-default-face :inherit hl-line)))
;; This determines the style of line numbers in effect. If set to `nil', line
;; numbers are disabled. For relative line numbers, set this to `relative'.
(setq display-line-numbers-type t)
;; If you use `org' and don't want your org files in the default location below,
;; change `org-directory'. It must be set before org loads!
(setq org-directory (concat (getenv "HOME") "/Workspace/org/"))
;; Whenever you reconfigure a package, make sure to wrap your config in an
;; `after!' block, otherwise Doom's defaults may override your settings. E.g.
;;
;; (after! PACKAGE
;; (setq x y))
;;
;; The exceptions to this rule:
;;
;; - Setting file/directory variables (like `org-directory')
;; - Setting variables which explicitly tell you to set them before their
;; package is loaded (see 'C-h v VARIABLE' to look up their documentation).
;; - Setting doom variables (which start with 'doom-' or '+').
;;
;; Here are some additional functions/macros that will help you configure Doom.
;;
;; - `load!' for loading external *.el files relative to this one
;; - `use-package!' for configuring packages
;; - `after!' for running code after a package has loaded
;; - `add-load-path!' for adding directories to the `load-path', relative to
;; this file. Emacs searches the `load-path' when you load packages with
;; `require' or `use-package'.
;; - `map!' for binding new keys
;;
;; To get information about any of these functions/macros, move the cursor over
;; the highlighted symbol at press 'K' (non-evil users must press 'C-c c k').
;; This will open documentation for it, including demos of how they are used.
;; Alternatively, use `C-h o' to look up a symbol (functions, variables, faces,
;; etc).
;;
;; You can also try 'gd' (or 'C-c c d') to jump to their definition and see how
;; they are implemented.
;; -----------------------------------------------------------------------------
;; Override doom configuration.
;; -----------------------------------------------------------------------------
;; Remove background from `avy'.
(after! avy (setq avy-background t))
(after! org
(defvar +x/org-capture-inbox-file "inbox.org")
(defvar +x/org-capture-reading-list-file "reading.org")
(add-to-list 'org-capture-templates
`("r" "Reading list" entry
(file+headline ,+x/org-capture-reading-list-file "Inbox")
"* %?"))
(add-to-list 'org-capture-templates
`("i" "Inbox" entry
(file ,+x/org-capture-inbox-file)
"* HOLD [#C] %?" :prepend t))
(setq org-todo-keywords
'((sequence
"HOLD(h)" ; This task needs doing eventually, but not yet
"NEXT(n)" ; A task that needs doing and is ready to do
"WEEK(w)" ; A task that is scheduled to work on this week
"TODO(d)" ; A task that is currently getting done
"WAIT(t)" ; Something external is holding up this task
"|"
"DONE(x)")); Task successfully completed
org-todo-keyword-faces
'(("HOLD" . +org-todo-onhold)
("NEXT" . +org-todo-active)
("WEEK" . org-todo)
("WAIT" . +org-todo-cancel)))
(add-to-list 'org-after-todo-state-change-hook
(lambda ()
(when (equal org-state "DONE")
(org-cut-subtree)))))
(after! org-agenda
(setq org-agenda-custom-commands
'(("w" "Agenda and tasklist for the week"
((agenda "")
(todo "HOLD")
(todo "NEXT")
(todo "WAIT")
(todo "WEEK")
(todo "TODO"))
((org-agenda-start-on-weekday 1)
(org-agenda-span 'fortnight)
(org-agenda-start-day "")
(org-deadline-warning-days 0)))
("r" "Agenda and tasklist for the weekly review"
((agenda "")
(todo "HOLD")
(todo "NEXT")
(todo "WAIT")
(todo "WEEK")
(todo "TODO"))
((org-agenda-start-on-weekday 1)
(org-agenda-span 'month)
(org-agenda-start-day "")
(org-deadline-warning-days 0))))))
;; Remove the silly doom newline advice.
(advice-remove 'newline-and-indent
#'+default--newline-indent-and-continue-comments-a)
;; Doom already uses whitespace-mode to highlight unexpected indentation. Add
;; some extras by defining an advice function.
(defadvice! +x-doom--highlight-unwanted-space-a nil
"Add some extra whitespace highlights to doom's opinion"
:after #'doom-highlight-non-default-indentation-h
(when (bound-and-true-p whitespace-mode)
(appendq! whitespace-style '(trailing empty))
(when (not (eq major-mode 'org-mode))
(appendq! whitespace-style '(lines-tail)))
(whitespace-mode +1)))
;; I actually like it when Emacs recenters the screen while scrolling. This may
;; be an issue in large files. When that becomes an issue add an exception here.
(setq scroll-conservatively 0)
;; -----------------------------------------------------------------------------
;; Additional coniguration for doom modules.
;; -----------------------------------------------------------------------------
;; Clangd LSP configuration.
(after! lsp-clangd
(setq lsp-clients-clangd-args
'("-j=8"
"--background-index"
"--clang-tidy"
"--completion-style=detailed"
"--header-insertion=never"
"--header-insertion-decorators=0"))
(set-lsp-priority! 'clangd 2))
;; Podman > docker.
(after! docker (setq docker-command "podman"))
(after! docker-tramp (setq docker-tramp-method "podman"
docker-tramp-docker-executable "podman"))
(after! dockerfile-mode (setq dockerfile-mode-command "podman"))
;; Fix git-gutter advice for magit.
(after! (:all git-gutter magit)
;; Update all visible buffers when (un)staging in the magit buffer.
(defadvice! +x-vc-gutter--update-visible-buffers-a (&rest _)
"Update vc-gutter in all visible buffers."
:after #'magit-stage
:after #'magit-unstage
(save-excursion
(dolist (buffer (doom-visible-buffers) nil)
(with-current-buffer buffer
(+vc-gutter-update-h))))))
;; Disable hl-line-mode everywhere.
(after! hl-line (setq global-hl-line-modes nil))
;; Don't use the LSP formatter.
(after! lsp (setq +format-with-lsp nil))
(after! lsp-mode
(lsp-register-client
(make-lsp-client :new-connection (lsp-tramp-connection "clangd")
:major-modes '(c-mode c++-mode)
:remote? t
:server-id 'clangd-remote)))
;; Python virtualenv configuration.
(after! lsp-pyright
(setq lsp-pyright-venv-path (concat (getenv "HOME") "/.virtualenvs")))
;; Always open magit in other window.
(after! magit
(setq magit-display-buffer-function #'magit-display-buffer-traditional))
;; Rainbow delimiters in all prog-mode buffers.
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode-enable)
;; Make pyvenv more convenient by defaulting to the correct directory.
(after! pyvenv
(setq pyvenv-default-virtual-env-name
(file-name-as-directory (pyvenv-workon-home))))
;; Make TRAMP easier to use for sudo on remote hosts.
(after! tramp
;; Proxy all sudo connections via an ssh connection to the provided hostname.
(add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
;; This rule is an exception to the above so that local sudo does not proxy
;; via ssh. This has to be added last so that it is the first element.
(add-to-list 'tramp-default-proxies-alist '("localhost" "\\`root\\'" nil)))
;; Configure undo-tree.
(after! undo-tree
;; Doom's default is to popup on the left, but undo-tree can get really wide.
;; It would actually be better to open it in its own window as is the default,
;; but then the diff window has annoying graphical glitches which for some
;; reason don't happen when a popup is used with the diff window configuration
;; hack further below.
(set-popup-rule! undo-tree-visualizer-buffer-name
:slot 2 :side 'top :size 20 :select t :quit t)
;; Doom just gives up on the diff visualizer, because it is impossible to work
;; around undo-tree's window management opinions in a consistent way. For now,
;; hack it to work in some sensible way.
(defadvice! +x-undo-tree--visualizer-show-diff-a (fn &rest args)
"Display the undo-tree diff buffer underneath undo-tree's parent buffer."
:around #'undo-tree-visualizer-show-diff
(defadvice! +x-undo-tree--visualizer-show-diff-split-window-a (&rest _)
"Hack `split-window' to open the diff window where we want it."
:filter-args #'split-window
(list (get-buffer-window undo-tree-visualizer-parent-buffer) nil 'below))
(apply fn args)
(advice-remove 'split-window
#'+x-undo-tree--visualizer-show-diff-split-window-a))
(setq
;; Don't keep persistent history - that's what git is for.
undo-tree-auto-save-history nil
;; This is nice, but this variables turns nil for some reason after leaving
;; undo-tree. A more consistent experience is to just toggle it with "d".
undo-tree-visualizer-diff nil))
;; The only thing I actually missed from helm.
(map! (:when (featurep! :completion vertico)
:map vertico-map
"C-l" (defun +x-vertico/directory-delete-one-word ()
"Delete one directory or word before point."
(interactive)
(vertico-directory-delete-word 1))))
;; Extra VTerm configuration.
(after! vterm
(setq vterm-shell "/bin/zsh"
vterm-max-scrollback 10000))
(map! :leader
(:prefix-map ("o" . "open")
(:when (featurep! :term vterm)
:desc "Project VTerm" "p" #'+vterm/project/other-window
:desc "Project VTerm (same window)" "P" #'+vterm/project/here
:desc "VTerm" "t" #'+vterm/other-window
:desc "VTerm (same window)" "T" #'+vterm/here)))
;; Tab key should indent and absolutely nothing more.
(after! yasnippet
(map! (:map yas-minor-mode-map
"<tab>" nil
"TAB" nil
"<C-return>" #'yas-expand)))
;; -----------------------------------------------------------------------------
;; Configuration for additional packages on top of doom and its modules.
;; -----------------------------------------------------------------------------
;; Nicer box comments.
(use-package! box-comments
:bind (("C-x C-'" . +x-box-comment/box-comment)))
;; A nicer interface for ripgrep. Note that since doom required rg we don't
;; consider the case if it's not installed.
(use-package! deadgrep
:bind (("C-x C-g" . deadgrep))
:init
(setq deadgrep-project-root-function
(defun +x-deadgrep-project-root-fn ()
"Provide a base directory for deadgrep."
(read-directory-name "Base directory: " nil default-directory t)))
:config
(map! (:map deadgrep-mode-map
"C-o" (defun +x-deadgrep/open-result-other-window ()
"Open the result in other window without changing to it."
(interactive)
(save-selected-window (deadgrep-visit-result-other-window)))
"RET" #'deadgrep-visit-result-other-window)))
;; Duplicate things.
(use-package! duplicate-thing
:bind (("M-C" . duplicate-thing)))
;; Debugger.
(use-package! gud
:defer t
:init
(setq-default
;; Use gdb-many-windows by default.
gdb-many-windows t
;; Display source file containing main.
gdb-show-main t)
:config
(setq gud-chdir-before-run nil))
;; P4 programming mode.
(use-package! p4_16-mode
:mode (("\\.p4\\'" . p4_16-mode)
("\\.p4i\\'" . p4_16-mode)))
;; Navigate between occurences of the same symbol.
(use-package! symbol-overlay
:bind (("M-RET" . symbol-overlay-put)
("M-n" . symbol-overlay-switch-forward)
("M-p" . symbol-overlay-switch-backward)
("<C-M-return>" . symbol-overlay-remove-all))
:hook (prog-mode . symbol-overlay-mode)
:config
;; Since we're reusing colours from orderless which only has four faces.
(setq symbol-overlay-faces '(orderless-match-face-0
orderless-match-face-1
orderless-match-face-2
orderless-match-face-3)))
;; It's the 21st century, a file explorer should be pretty.
(use-package! treemacs-icons-dired
:after dired
:hook (dired-mode . treemacs-icons-dired-mode))
;; Kill line when calling kill-region without a selected region.
(use-package! whole-line-or-region
:config
(define-key whole-line-or-region-local-mode-map [remap comment-dwim] nil)
(whole-line-or-region-global-mode +1))
;; Defer loading of local configuration library.
(use-package! x-lib
:defer t)
;; Load `yank-indent' without deferring.
(use-package! yank-indent
:config
(advice-add 'yank :after #'+x-yank-indent-a)
(advice-add 'yank-pop :after #'+x-yank-indent-a)
(advice-add 'consult-yank-pop :after #'+x-yank-indent-a))
;; -----------------------------------------------------------------------------
;; Global configuration.
;; -----------------------------------------------------------------------------
(setq-default
;; Don't keep dired directories around when navigating.
dired-kill-when-opening-new-dired-buffer t
;; Standard fill-column width.
fill-column 100
;; Do not use tab characters for indentation.
indent-tabs-mode nil
;; Kill whole line when point at beginning of line.
kill-whole-line t
;; Always indent. Why would I ever want to insert a tab?
tab-always-indent t
;; Default indentation size - applies even when indent-tabs-mode is nil.
tab-width 8)
;; Highlight lines that are too long in whitespace mode. We set this with a hook
;; after local variables as fill-column is often set through dir-locals.
(add-hook! 'hack-local-variables-hook
(defun +x-whitespace--update-whitespace-line-column-h ()
"Change `whitespace-line-column' to that of `fill-column'."
(setq-local whitespace-line-column fill-column)))
;; CamelCase as separate words.
(global-subword-mode +1)
;; Doom emacs recommends `visual-line-mode' as it claims wrapping is expensive.
(global-visual-line-mode +1)
;; But "C-e" behaves differently in `visual-line-mode' than when wrapping. It
;; goes to the end of the visual line, not the logical line. This can be fixed
;; by defining the following aliases.
(defalias #'end-of-visual-line #'end-of-line)
(defalias #'beginning-of-visual-line #'beginning-of-line)
;; Enable the fill column indicator.
(add-hook 'prog-mode-hook #'display-fill-column-indicator-mode)
;; Allow Emacs in WSL to open links in a browser on the Windows host.
(when (and (eq system-type 'gnu/linux)
(string-match
"Linux.*Microsoft.*Linux"
(shell-command-to-string "uname -a")))
(setq
browse-url-generic-program "/mnt/c/Windows/System32/cmd.exe"
browse-url-generic-args '("/c" "start")
browse-url-browser-function #'browse-url-generic)
(defun +x/browse-url-of-dired-file ()
"In Dired, ask a WWW browser to display the file named on this line."
(interactive)
(let ((tem (dired-get-filename t t)))
(if tem
(browse-url-of-file (concat "file://///wsl$/"
(getenv "WSL_DISTRO_NAME")
(expand-file-name tem)))
(error "No file on this line"))))
(map! (:map dired-mode-map
"W" #'+x/browse-url-of-dired-file)))
;; -----------------------------------------------------------------------------
;; Global keybindings.
;; -----------------------------------------------------------------------------
(map!
"C-M-\\" #'+x/indent-region-or-buffer
"C-x k" #'+x/kill-buffer
"C-<" #'+x/scroll-down-one
"C->" #'+x/scroll-up-one
"M-Q" #'+x/unfill-paragraph
(:map org-mode-map
"C-c C-;" #'+x/org-insert-link-with-title)
;; Faster navigation with avy.
"M-g M-c" #'avy-goto-char
"M-g M-v" #'avy-goto-char-2
"M-g M-l" #'avy-goto-line
"M-g M-e" #'avy-goto-word-0
"M-g M-w" #'avy-goto-word-1
"M-g M-t" #'avy-goto-char-timer
"M-g M-j" #'avy-resume
(:map isearch-mode-map
"C-'" #'avy-isearch)
;; Create binding before dired is loaded.
"C-x C-j" #'dired-jump
;; More convenient window switching.
"M-o" #'other-window
"C-x o" #'other-popup)