;;; init.el --- GNU Emacs Initialization File -*- lexical-binding: t; -*- ;; Author: Andrew Scott ;; Keywords: convenience, tools ;; URL: https://codeberg.org/andyscott/dotfiles ;; This file is not part of GNU Emacs. ;; Copyright (c) 2023 Andrew Scott ;; MIT No Attribution ;; Permission is hereby granted, free of charge, to any person obtaining a copy of this ;; software and associated documentation files (the "Software"), to deal in the Software ;; without restriction, including without limitation the rights to use, copy, modify, ;; merge, publish, distribute, sublicense, and/or sell copies of the Software, and to ;; permit persons to whom the Software is furnished to do so. ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, ;; INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A ;; PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ;; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ;;; Commentary: ;; My Emacs initialization file. ;;; Code: ;; Profile init time ;; Replace `emacs-startup-hook' w/ `elpaca-after-init-hook' to profile `Elpaca' (add-hook 'emacs-startup-hook (lambda () (message "Emacs loaded in %s with %d garbage collections." (format "%.2f seconds" (float-time (time-subtract (current-time) before-init-time))) gcs-done))) ;; View profiler report after init ;; (profiler-start 'cpu+mem) ;; (add-hook 'elpaca-after-init-hook (lambda () (profiler-stop) (profiler-report))) ;; Profile function performance ;; (require 'elp) ;; (with-eval-after-load file ;; (elp-instrument-package file)) ;; (add-hook 'elpaca-after-init-hook ;; (lambda () (elp-results) (elp-restore-package (intern file)))) ;; Elpaca (defvar elpaca-installer-version 0.7) (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory)) (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory)) (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory)) (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git" :ref nil :depth 1 :files (:defaults "elpaca-test.el" (:exclude "extensions")) :build (:not elpaca--activate-package))) (let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) (order (cdr elpaca-order)) (default-directory repo)) (add-to-list 'load-path (if (file-exists-p build) build repo)) (unless (file-exists-p repo) (make-directory repo t) (when (< emacs-major-version 28) (require 'subr-x)) (condition-case-unless-debug err (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*")) ((zerop (apply #'call-process `("git" nil ,buffer t "clone" ,@(when-let ((depth (plist-get order :depth))) (list (format "--depth=%d" depth) "--no-single-branch")) ,(plist-get order :repo) ,repo)))) ((zerop (call-process "git" nil buffer t "checkout" (or (plist-get order :ref) "--")))) (emacs (concat invocation-directory invocation-name)) ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch" "--eval" "(byte-recompile-directory \".\" 0 'force)"))) ((require 'elpaca)) ((elpaca-generate-autoloads "elpaca" repo))) (progn (message "%s" (buffer-string)) (kill-buffer buffer)) (error "%s" (with-current-buffer buffer (buffer-string)))) ((error) (warn "%s" err) (delete-directory repo 'recursive)))) (unless (require 'elpaca-autoloads nil t) (require 'elpaca) (elpaca-generate-autoloads "elpaca" repo) (load "./elpaca-autoloads"))) (add-hook 'after-init-hook #'elpaca-process-queues) (elpaca `(,@elpaca-order)) ;; Ensure `Elpaca' doesn't pull built-in packages from source (defmacro use-feature (name &rest args) "Like `use-package' but accounting for asynchronous installation. NAME and ARGS are in `use-package'. Credit to @progfolio: https://github.com/progfolio/.emacs.d?tab=readme-ov-file#use-package" (declare (indent defun)) `(use-package ,name :ensure nil ,@args)) (elpaca elpaca-use-package (require 'elpaca-use-package) (elpaca-use-package-mode) (setopt use-package-always-ensure t ;; REVIEW - apparently `use-package-always-pin' doesn't work with `Elpaca' ;; TODO - remove stats when done profiling startup use-package-compute-statistics t)) (if debug-on-error (setopt use-package-verbose t use-package-expand-minimally nil use-package-compute-statistics t) (setopt use-package-verbose nil use-package-expand-minimally t)) ;; Wait for `Elpaca' to process current queue (elpaca-wait) ;;; Built-in Packages: ;; Revert a buffer if the file has changed (use-feature autorevert :config (setopt auto-revert-interval 10) (global-auto-revert-mode) :defer 5 :init (setopt auto-revert-interval 0.01)) (use-feature bibtex :after (ebib) :config (setopt bibtex-autokey-additional-names "EtAl" bibtex-autokey-edit-before-use t ; "t" by default bibtex-autokey-expand-strings t bibtex-autokey-name-case-convert-function #'capitalize bibtex-autokey-name-year-separator "" bibtex-autokey-names-stretch 1 bibtex-autokey-titlewords 2 bibtex-autokey-titlewords-stretch 1 bibtex-autokey-titleword-length #'infty bibtex-autokey-titleword-separator "" bibtex-autokey-titleword-case-convert-function #'capitalize bibtex-autokey-year-length 4 bibtex-autokey-year-length 4 bibtex-autokey-year-title-separator "")) (use-feature c-ts-mode :commands (c-ts-mode) :config (setopt c-ts-mode-indent-style 'linux c-ts-mode-indent-offset 8 indent-tabs-mode t)) (use-feature c++-ts-mode :commands (c++-ts-mode) :config (setopt c++-ts-mode-indent-style 'gnu c-ts-mode-indent-offset 4)) ;; Customize compilation & related buffers (use-feature compile :commands (compile recompile) :config (setopt compilation-ask-about-save nil compilation-scroll-output 'first-error) :hook (compilation-filter . ansi-color-compilation-buffer)) ;; Write external customizations to /dev/null (use-feature cus-edit :init (setopt custom-file null-device)) ;; Library for creating/customizing user options (use-feature custom :config (setopt custom-safe-themes t)) ;; File manager (use-feature dired :commands (dired) :config (setopt dired-kill-when-opening-new-dired-buffer t dired-mouse-drag-files t)) ;; Customize fill column indicator (use-feature display-fill-column-indicator :hook ((conf-mode markdown-mode prog-mode) . display-fill-column-indicator-mode)) ;; Customize line number display (use-feature display-line-numbers :hook ((conf-mode prog-mode) . display-line-numbers-mode)) ;; Customize LSP options (use-feature eglot :bind (:map eglot-mode-map ("C-c e a" . eglot-code-actions) ("C-c e o" . eglot-code-actions-organize-imports) ("C-c e r" . eglot-rename) ("C-c e f" . eglot-format)) :config (add-to-list 'eglot-server-programs '(astro-mode . ("astro-ls" "--stdio" :initializationOptions (:typescript (:tsdk "/usr/lib/node_modules/typescript/lib"))))) (add-to-list 'eglot-server-programs '((rust-ts-mode rust-mode) . ("rustup" "run" "stable" "rust-analyzer" :initializationOptions (:check (:command "clippy"))))) (setopt eglot-autoshutdown t eglot-ignored-server-capabilities '(:colorProvider ; "Decorate color references" ;; :documentFormattingProvider ; "Format buffer" ;; :documentHighlightProvider ; "Highlight symbols automatically" :documentOnTypeFormattingProvider ; "On-type formatting" ;; :documentRangeFormattingProvider ; "Format portion of buffer" :hoverProvider ; "Documentation on hover" :inlayHintProvider ; "Inlay hints" )) :hook ((astro-mode bash-ts-mode c-ts-mode c++-ts-mode python-ts-mode rust-ts-mode sh-script zig-mode) . eglot-ensure)) ;; Automatic parenthesis/brackets matching (use-feature elec-pair :hook ((conf-mode markdown-mode prog-mode) . electric-pair-mode)) ;; Options defined in C source code & other misc. global customizations (use-feature emacs :bind (:map global-map ;; Disable `suspend-frame' shortcut (currently overwritten by undo-fu anyway) ("C-z" . nil) ("C-x k" . my-kill-buffer) ;; Escape behaves like C-g ("" . keyboard-escape-quit) ;; Resizing buffers ("S-C-" . shrink-window-horizontally) ("S-C-" . enlarge-window-horizontally) ("S-C-" . shrink-window) ("S-C-" . enlarge-window)) :config (setopt completion-ignore-case t cursor-type 'bar enable-recursive-minibuffers t fill-column 79 history-delete-duplicates t minibuffer-prompt-properties ; Disable prompt in minibuffer '(read-only t cursor-intangible t face minibuffer-prompt) read-buffer-completion-ignore-case t scroll-step 1 scroll-conservatively 1000 scroll-preserve-screen-position t sentence-end-double-space nil tab-always-indent 'complete use-short-answers t) :hook (minibuffer-setup . cursor-intangible-mode) :init (defun my-kill-buffer () (interactive) (catch 'quit (save-window-excursion (let (done) (when (and buffer-file-name (buffer-modified-p)) (while (not done) (let ((response (read-char-choice (format "Save file %s? (y, n, d, q) " (buffer-file-name)) '(?y ?n ?d ?q)))) (setq done (cond ((eq response ?q) (throw 'quit nil)) ((eq response ?y) (save-buffer) t) ((eq response ?n) (set-buffer-modified-p nil) t) ((eq response ?d) (diff-buffer-with-file) nil)))))) (kill-buffer (current-buffer)) (force-mode-line-update))))) ;; For Vertico (defun crm-indicator (args) (cons (format "[CRM%s] %s" (replace-regexp-in-string "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" crm-separator) (car args)) (cdr args))) (advice-add #'completing-read-multiple :filter-args #'crm-indicator)) ;; Customize file handling & automatic backups (use-feature files :config (let ((auto_save_directory (concat xdg_cache_home "auto-save-list/"))) (unless (file-directory-p auto_save_directory) (make-directory auto_save_directory)) (setopt backup-directory-alist `((".*" . ,auto_save_directory)) auto-save-list-file-prefix (concat auto_save_directory "saves-") auto-save-file-name-transforms `((".*" ,auto_save_directory t)))) (setopt backup-by-copying t confirm-kill-processes nil delete-old-versions t kept-new-versions 5 kept-old-versions 3 require-final-newline t version-control t) ;; Treesit doesn't play nice in Emacs 29 ;; REVIEW: Putting this here for now because `major-mode-remap-alist' belongs to `files' (add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode)) (add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))) ;; Customize syntax checking (use-feature flymake :bind (:map flymake-mode-map ("C-c f d" . flymake-show-buffer-diagnostics) ("C-c f D" . flymake-show-project-diagnostics) ("C-c f n" . flymake-goto-next-error) ("C-c f p" . flymake-goto-prev-error))) ;; Customize spell checking (use-feature flyspell :hook (((git-commit-mode org-mode) . flyspell-mode)) :init (setopt flyspell-use-meta-tab nil)) ;; Customize frame behavior/cursor blinking (use-feature frame :config (setopt blink-cursor-delay 1.0 blink-cursor-interval 0.75)) (use-feature help :config (setopt help-enable-variable-value-editing t help-window-select t) :defer 3) (use-feature minibuffer :config (setopt read-file-name-completion-ignore-case t)) ;; Ensure UTF-8 terminal encoding (use-feature mule :config (unless (display-graphic-p) (set-terminal-coding-system 'utf-8))) ;; Customize mouse wheel (use-feature mwheel :config (setopt mouse-wheel-follow-mouse t mouse-wheel-progressive-speed nil mouse-wheel-scroll-amount '(2 ((shift) . 1)))) ;; Customize comment insertion (use-feature newcomment :config (setopt comment-style 'multi-line)) ;; Visualize matching parenthesis/brackets (use-feature paren :hook ((conf-mode markdown-mode prog-mode) . show-paren-mode)) ;; Enable smooth mouse wheel scrolling when available (when (fboundp 'pixel-scroll-precision-mode) (use-feature pixel-scroll :config (pixel-scroll-precision-mode) :defer 1)) ;; Customize project management (use-feature project :commands (project-find-dir project-find-file project-switch-project) :config (setopt project-vc-extra-root-markers '("Cargo.toml"))) ;; Customize handling of recent files (use-feature recentf :config (setopt recentf-auto-cleanup 600 recentf-max-menu-items 100 recentf-max-saved-items 100 recentf-save-file (expand-file-name "recentf" xdg_cache_home)) (recentf-mode)) ;; Customize saving mini-buffer history (use-feature savehist :config (setopt savehist-additional-variables '(compile-command kill-buffer kill-ring) savehist-autosave-interval 120 savehist-file (expand-file-name "history" xdg_cache_home)) (savehist-mode) :defer 1) ;; Customize saving place in files (use-feature saveplace :config (setopt save-place-file (expand-file-name "places" xdg_cache_home)) (save-place-mode)) ;; Unload `seq' before `Elpaca' starts building ;; https://github.com/progfolio/elpaca/issues/216#issuecomment-1868444883 (defun +elpaca-unload-seq (e) (and (featurep 'seq) (unload-feature 'seq t)) (elpaca--continue-build e)) (defun +elpaca-seq-build-steps () (append (butlast (if (file-exists-p (expand-file-name "seq" elpaca-builds-directory)) elpaca--pre-built-steps elpaca-build-steps)) (list '+elpaca-unload-seq 'elpaca--activate-package))) (use-package seq :ensure `(seq :build ,(+elpaca-seq-build-steps))) (use-feature shell :hook (shell-mode . ansi-color-for-comint-mode-on)) ;; "A grab-bag of basic Emacs commands" (use-feature simple :config (setopt indent-tabs-mode nil read-extended-command-predicate #'command-completion-default-include-p) :hook (((conf-mode prog-mode) . column-number-mode) (text-mode . auto-fill-mode))) ;; Customize tree-sitter parsing (use-feature treesit :config (setopt treesit-font-lock-level 4) :defer 1) ;; Remember to take breaks (use-feature type-break :config (setopt type-break-keystroke-threshold '(nil . nil)) :commands (type-break-mode)) ;; Customize whitespace visualization & cleanup (use-feature whitespace :config (add-hook 'before-save-hook #'whitespace-cleanup) (setopt whitespace-line-column nil whitespace-style '(lines-char missing-newline-at-eof space-after-tab space-before-tab tabs trailing)) :hook ((conf-mode prog-mode text-mode) . whitespace-mode)) ;;; Packages: ;; `other-window' replacement (use-package ace-window :commands (ace-window) :bind ("C-c w" . ace-window)) ;; Shows current/total matches in mode line (use-package anzu :config (global-anzu-mode) :defer 3) ;; Auto-format (use-package apheleia :defer t) (use-package biblio :after (ebib) :config (setopt biblio-bibtex-use-autokey t)) ;; Extensions for `completion-at-point' (use-package cape :bind ("M-p" . cape-prefix-map)) ;; TODO: pick a theme ;; Catppuccin theme (use-package catppuccin-theme :disabled :config (setopt catppuccin-flavor 'macchiato) ;; (catppuccin-reload) ;; (load-theme 'catppuccin t) ) ;; C/C++ formatting ;; Ad hoc format file: clang-format -style=webkit -dump-config > .clang-format (use-package clang-format :commands (clang-format clang-format-buffer clang-format-region) :config (setopt clang-format-fallback-style "webkit" clang-format-style "file")) ;; Search and navigation (use-package consult :bind (;; C-c bindings in `mode-specific-map' ("C-c M-x" . consult-mode-command) ("C-c h" . consult-history) ("C-c k" . consult-kmacro) ("C-c m" . consult-man) ("C-c i" . consult-info) ([remap Info-search] . consult-info) ;; C-x bindings in `ctl-x-map' ("C-x M-:" . consult-complex-command) ; orig. repeat-complex-command ("C-x b" . consult-buffer) ; orig. switch-to-buffer ("C-x 4 b" . consult-buffer-other-window) ; orig. switch-to-buffer-other-window ("C-x 5 b" . consult-buffer-other-frame) ; orig. switch-to-buffer-other-frame ("C-x r b" . consult-bookmark) ; orig. bookmark-jump ("C-x p b" . consult-project-buffer) ; orig. project-switch-to-buffer ("C-x M-r" . consult-recent-file) ;; Custom M-# bindings for fast register access ("M-#" . consult-register-load) ("M-'" . consult-register-store) ; orig. abbrev-prefix-mark (unrelated) ("C-M-#" . consult-register) ;; Other custom bindings ("M-y" . consult-yank-pop) ; orig. yank-pop ;; M-g bindings in `goto-map' ("M-g e" . consult-compile-error) ("M-g f" . consult-flymake) ; Alternative: consult-flycheck ("M-g g" . consult-goto-line) ; orig. goto-line ("M-g M-g" . consult-goto-line) ; orig. goto-line ("M-g o" . consult-outline) ; Alternative: consult-org-heading ("M-g m" . consult-mark) ("M-g k" . consult-global-mark) ("M-g i" . consult-imenu) ("M-g I" . consult-imenu-multi) ;; M-s bindings in `search-map' ("M-s d" . consult-find) ("M-s D" . consult-locate) ("M-s g" . consult-grep) ("M-s G" . consult-git-grep) ("M-s r" . consult-ripgrep) ("M-s l" . consult-line) ("M-s L" . consult-line-multi) ("M-s k" . consult-keep-lines) ("M-s u" . consult-focus-lines) ;; Isearch integration ("M-s e" . consult-isearch-history) :map isearch-mode-map ("M-e" . consult-isearch-history) ; orig. isearch-edit-string ("M-s e" . consult-isearch-history) ; orig. isearch-edit-string ("M-s l" . consult-line) ; needed by consult-line to detect isearch ("M-s L" . consult-line-multi) ; needed by consult-line to detect isearch ;; Minibuffer history :map minibuffer-local-map ("M-s" . consult-history) ; orig. next-matching-history-element ("M-r" . consult-history) :map as/org-prefix-map ; orig. previous-matching-history-element ("h" . consult-org-heading) ("a" . consult-org-agenda)) :config (setopt consult-narrow-key "<") :init ;; Register formatting (setopt register-preview-delay 0.6 register-preview-function #'consult-register-format) ;; Register preview window (advice-add #'register-preview :override #'consult-register-window) ;; Select xref locations with preview (setopt xref-show-xrefs-function #'consult-xref xref-show-definitions-function #'consult-xref)) (use-package consult-dir :after (consult) :bind (("C-x C-d" . consult-dir) :map vertico-map ("C-x C-d" . consult-dir) ("C-x C-j" . consult-dir-jump-file))) ;; UI for the workspace/symbols procedure calls (use-package consult-eglot :after (eglot) :bind (:map eglot-mode-map ("C-c e s" . consult-eglot-symbols))) ;; In-buffer completion with popup menu (use-package company :disabled :bind (:map company-active-map ("RET" . nil) ("[return]" . nil) ("TAB" . company-complete-selection) ("" . company-complete-selection) ("C-n" . company-select-next) ("C-p" . company-select-previous)) :config (setopt company-global-modes '(not gud-mode not comint-mode) company-tooltip-align-annotations t) :hook (prog-mode . company-mode)) ;; In-buffer completion with popup menu (use-package corfu :config (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster) (advice-add 'eglot-completion-at-point :around #'cape-wrap-noninterruptible) (setopt corfu-auto nil corfu-cycle t corfu-preview-current nil) (global-corfu-mode) (corfu-history-mode) (corfu-popupinfo-mode) :defer 3 :ensure (corfu :files (:defaults "extensions/*"))) (use-package ebib ;;; Functions: ;; ebib-list-recent ; list entries added within the given # of days ;; ebib-download-url ; attempt to download file, rename, and save to target directory, filename determined by `ebib-name-transform-function' ;; ebib-import-file ; import a local file, file is renamed and moved, works for more than pdf, name deterined by `ebib-name-transform-function' ;;; ebib-biblio ;; ebib-biblio-selection-import ; function to import entry from biblio into ebib ;; ebib-biblio-import-doi ; function to import an entry while in ebib UI, can also choose different key ;;; keymaps ;; ebib-index-mode-map ;; ebib-entry-mode-map ;; ebib-strings-mode-map ;; ebib-multiline-mode-map ;; ebib-search-map ;; ebib-filters-map ;; ebib-keywords-map ;; ebib-reading-list-map ;; ebib-log-mode-map :commands (ebib) :config ;; ebib-autogenerate-keys ; default t (setopt ebib-bibtex-dialect 'biblatex ebib-use-timestamp t ; create timestamp when entry added to db, note that "When importing or exporting entries, existing timestamps are overwritten." ebib-filters-default-file (expand-file-name "ebib-filters" user-emacs-directory) ; file location to save filters ebib-allow-identical-fields t ; merge repeated fields into one, separated by `ebib-keywords-separator' ebib-file-associations '(("pdf" . nil) ("ps" . "gs")) ; specify which programs to use for which file types, or remove to open in emacs ebib-import-target-directory "~/Nextcloud/Library/" ; set directory for `ebib-download-url' and `ebib-import-file' ;; ebib-default-directory "~/Nextcloud/Library/"; "This mainly determines which directory is the default directory when reading a file name from the user" ebib-preload-bib-files '("~/Nextcloud/Library/bib/library.bib") ; autoload list of .bib files when started ;; ebib-bib-search dirs ; specify directory to search for multiple bib files when starting ;; ebib-extra-fields ; probably not needed ;;; UI ;; ebib-index-columns ; change displayed UI fields ;; ebib-field-transformation-functions ; new/change UI fields ;; ebib-field-sort-functions-alist ; customize UI sorting ;; ebib-edit-fields-functions ; disable/enable completion for different UI fields ;; ebib-multiline-display-function ; customize multiline values in UI ;; ebib-multiline-display-max-lines ; customize multiline values in UI ;; ebib-hidden-fields ; control which UI fields are hidden ;; ebib-save-indent-as-bibtex ; spaces instead of tabs in bib files, uses value of `bibtex-entry-offset' and `bibtex-field-indentation' to compute # of spaces ;; ebib-biblatex-ineritances ; customize biblatex inheritance ;;; Editing ;; ebib-save-xrefs-first ; must be enabled for cross-referencing to work, unless sort order changed? ;; ebib-sort-order ; change sort order of bib file - default is by key - make sure to unset `ebib-save-xrefs-first' ;; ebib-multiline-major-mode ; customize major mode for `ebib-multiline-edit-mode' ;; ebib-citation-commands ; might need to customize if using BibTex, which I'm not ;; ebib-citations-insert-multiple ; enable adding multiple citations at once, likely not needed ;; ebib-filters-ignore-case ; case-insensitive search filters are default ;;; Downloading/Filenames ;; ebib-name-transform-function ; modify file name that ebib searches for when associating a file with an entry ;; ebib-url-download-transformations ; sets how URLs are converted when attempting to download ;;; Notes ;; ebib-notes-storage 'multiple-notes-per-file ; probabably set to `multiple-notes-per-file' for org? ;; ebib-notes-locations ; files/directories containing notes ;; ebib-notes-default-file ; not used if creating note via org capture, but can still be set ;;; Org-capture ;; ebib-notes-use-org-capture ; set to desired keybind for ebib to skip org capture selection and use the one specified ;; ebib-org-capture ; function to call org capture from ebib ;;; Reading list ;; ebib-reading-list-file ; location of reading list ;; ebib-reading-list-todo-marker ; change default todo state for reading list items ;; ebib-reading-list-template ; change format of reading list items, available options in `ebib-reading-list-template-specifiers' ;; ebib-reading-list-add-item-function ; change where new items should be added ;; ebib-reading-list-remove-item ; by default only set to `ebib-reading-list-mark-item-as-done' - doesn't actually remove anything ;;; Keywords ;; ebib-keywords ; specify named keywords always available for completion ;;; Display options ;; ebib-windows ; customization group to change display ;;; Copying from ebib ;; ebib-reference-templates and ebib-citation-template ; change how entries are copied into kill ring ;;; org-ebib ;; org-ebib-link-type 'multiple-notes-per-file ; specify type of link produced by `org-store-link' on bib entries ) ) (use-package ebib-biblio :after (ebib biblio) :bind (:map ebib-index-mode-map ("B" . ebib-biblio-import-doi)) (:map biblio-selection-mode-map ("e" . ebib-biblio-selection-import)) :ensure nil) ;; Use `emacs-lsp-booster' with `Eglot' (use-package eglot-booster :after eglot :config (eglot-booster-mode) :ensure (eglot-booster :host github :repo "jdtsmith/eglot-booster")) ;; Command menu for items around point (use-package embark :bind (("C-." . embark-act) ("C-;" . embark-dwim) ("C-h B" . embark-bindings)) :config ;; Hide the Embark live/completions buffer mode line (add-to-list 'display-buffer-alist '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*" nil (window-parameters (mode-line-format . none)))) :defer 3 :init (setopt prefix-help-command #'embark-prefix-help-command)) ;; Integration between `embark' and `consult' (use-package embark-consult :hook (embark-collect-mode . consult-preview-at-point-mode)) ;; Startup screen (use-package enlight :after (grid) :config (setopt enlight-content (grid-get-row `(,(enlight-menu '(("Files" ("Find File" find-file "f") ("Recent Files" consult-recent-file "r") ("Projects" project-switch-project "p") ("Treemacs" treemacs "t")))) " " ,(enlight-menu '(("Org" ("My Agenda" (org-agenda nil "y") "y") ("Work Agenda" (org-agenda nil "w") "w") ("Time blocking" org-timeblock "b") ("Match Tags" (org-agenda nil "m") "m")))) " " ,(enlight-menu `(("Settings" ("Elpaca" elpaca-manager "L") ("Emacs" (progn (find-file ,user-init-file)) "I") ("Enlight" (progn (find-file ,user-init-file) (goto-char (point-min)) (search-forward "use-package enlight")) "E") ("Org" (progn (find-file ,(replace-regexp-in-string "init.el" "lisp/init-org.el" user-init-file))) "O"))))))) (enlight-open)) ;; Correction functions for `flyspell' (use-package flyspell-correct :after (flyspell) :bind (:map flyspell-mode-map ("C-M-." . flyspell-correct-wrapper))) ;; Grid for `enlight' (use-package grid :ensure (:host github :repo "ichernyshovvv/grid.el")) ;; Highlight keywords (use-package hl-todo :bind (:map hl-todo-mode-map ("C-c C-t p" . hl-todo-previous) ("C-c C-t n" . hl-todo-next) ("C-c C-t o" . hl-todo-occur) ("C-c C-t i" . hl-todo-insert)) :config (setopt hl-todo-highlight-punctuation ":!?" hl-todo-keyword-faces '(("DEBUG" warning bold) ("HACK" warning bold) ("FIXME" error bold) ("NOTE" homoglyph italic) ("REVIEW" homoglyph italic) ("TODO" homoglyph bold))) :ensure (hl-todo :depth nil) :hook (prog-mode . hl-todo-mode)) ;; Search `hl-todo' buffers with `consult' (use-package consult-todo :after (hl-todo) :bind (:map hl-todo-mode-map ("C-c C-t c" . consult-todo) ("C-c C-t C-c" . consult-todo-all))) ;; Icons for `corfu' (use-package kind-icon :disabled :after (corfu) :config ;; Using nerd icons below ;; (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter) (setopt kind-icon-default-face 'corfu-default)) ;; Git ;; Required by `magit' for menus (use-package transient :defer t) (use-package magit :bind ("C-M-;" . magit-status) :config (setopt magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1 transient-history-file (expand-file-name "transient-history" xdg_cache_home))) ;; Show `hl-todo' keywords in `magit' status buffers (use-package magit-todos :hook (magit-mode . magit-todos-mode)) ;; Better annotations (use-package marginalia :config (marginalia-mode) :defer 1) ;; File format/markup support (use-package markdown-mode :bind (:map markdown-mode-map ("C-M-i" . fill-paragraph)) :commands (markdown-mode)) ;; Better C++ font locking (use-package modern-cpp-font-lock :hook (c++-ts-mode . modern-c++-font-lock-mode)) ;; Mode line (use-package moody :config (setq x-underline-at-descent-line t) (moody-replace-eldoc-minibuffer-message-function) (moody-replace-mode-line-buffer-identification) (moody-replace-mode-line-front-space) (moody-replace-vc-mode) :defer 1) ;; Menu for minor modes in `minor-mode-list' (use-package minions :after (moody) :config (minions-mode)) ;; Nerd icons for various modes & packages (use-package nerd-icons :defer 1) (use-package nerd-icons-completion :after (nerd-icons marginalia) :config (nerd-icons-completion-mode) :hook (marginalia-mode . nerd-icons-completion-marginalia-setup)) (use-package nerd-icons-corfu :after (nerd-icons corfu) :config (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)) (use-package nerd-icons-dired :hook (dired-mode . nerd-icons-dired-mode)) (use-package nerd-icons-ibuffer :hook (ibuffer-mode . nerd-icons-ibuffer-mode)) ;; TODO: pick a theme (use-package nimbus-theme :config (load-theme 'nimbus)) ;; EPUB (use-package nov :config (setopt nov-text-width 100) :mode ("\\.epub\\'" . nov-mode)) ;; Completion style (use-package orderless :config (setopt completion-styles '(orderless basic) completion-category-overrides '((file (styles basic partial-completion))) orderless-matching-styles '(orderless-regexp orderless-literal orderless-initialism)) :defer 1) ;; Better PDF viewing and navigation (use-package pdf-tools :config (setopt pdf-info-epdfinfo-program (expand-file-name "pdf-tools/server/epdfinfo" elpaca-builds-directory) pdf-view-resize-factor 1.1 pdf-view-display-size 'fit-page) :ensure (pdf-tools :pre-build ("./server/autobuild") :files (:defaults ("lisp/*" "server/epdfinfo"))) :hook (pdf-view-mod . pdf-view-midnight-minor-mode) :mode ("\\.pdf\\'" . pdf-view-mode)) ;; Project Management - disabled, see `project' built-in package (use-package projectile :disabled :bind ("C-c p" . projectile-command-map) :config (setopt projectile-known-projects-file (expand-file-name "projectile-bookmarks" xdg_cache_home) projectile-project-search-path '(("~/Nextcloud/Projects/src/" . 2))) (add-to-list 'projectile-globally-ignored-directories "*node_modules") (projectile-mode)) ;; Visualize color names (use-package rainbow-mode :hook ((conf-mode prog-mode toml-ts-mode) . rainbow-mode)) ;; `gud' rewrite with additional features (use-package realgud :defer t) ;; `realgud' front-end for LLDB (use-package realgud-lldb :defer t) (use-package rust-mode :init (setopt rust-mode-treesitter-derive t) :commands (rust-mode)) (use-package treemacs :defer t :commands (treemacs) :config (setopt treemacs-eldoc-display 'simple treemacs-hide-dot-git-directory nil treemacs-is-never-other-window t treemacs-persist-file (expand-file-name "treemacs-persist" xdg_cache_home) treemacs-text-scale 2 treemacs-width-is-initially-locked t) (add-to-list 'treemacs-litter-directories "./zig-cache") (treemacs-resize-icons 22) (treemacs-follow-mode t) (treemacs-filewatch-mode t) (treemacs-fringe-indicator-mode 'always) (treemacs-git-commit-diff-mode t) (treemacs-git-mode 'deferred) ;; (treemacs-hide-gitignored-files-mode nil) (treemacs-indent-guide-mode) :init (bind-keys :prefix-map as/treemacs-prefix-map :prefix "C-c t" ("0" . treemacs-select-window) ("1" . treemacs-delete-other-windows) ("t" . treemacs) ("d" . treemacs-select-directory) ("B" . treemacs-bookmark) ("C-f" . treemacs-find-file) ("M-t" . treemacs-find-tag))) (use-package treemacs-magit :after (treemacs magit)) ;; Automatically download tree-sitter grammars (use-package treesit-auto :defer 1 :config (setopt treesit-auto-install t) (setq as/c-tsauto-config (make-treesit-auto-recipe :lang 'c :ts-mode 'c-ts-mode :remap 'c-mode :url "https://github.com/tree-sitter/tree-sitter-c" :revision "v0.20.7" :ext "\\.c\\'")) (setq as/cpp-tsauto-config (make-treesit-auto-recipe :lang 'cpp :ts-mode 'c++-ts-mode :remap 'c++-mode :url "https://github.com/tree-sitter/tree-sitter-cpp" :revision "v0.21.0" :ext "\\.cpp\\'")) (add-to-list 'treesit-auto-recipe-list as/c-tsauto-config) (add-to-list 'treesit-auto-recipe-list as/cpp-tsauto-config) ;; (push as/c-tsauto-config treesit-auto-recipe-list) ;; (push as/cpp-tsauto-config treesit-auto-recipe-list) (treesit-auto-add-to-auto-mode-alist 'all) (global-treesit-auto-mode)) ;; Better undo/redo (use-package undo-fu :bind (("C-z" . undo-fu-only-undo) ("C-S-z" . undo-fu-only-redo)) :defer 3) (use-package undo-fu-session :after (undo-fu) :config (setopt undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'" "/git-rebase-todo\\'") undo-fu-session-directory (concat xdg_cache_home "undo-fu-session/")) (undo-fu-session-global-mode)) ;; Mini-buffer completion UI (use-package vertico :config (setopt vertico-cycle t vertico-count 10 vertico-resize t vertico-scroll-margin 1) :init (vertico-mode)) ;; Web and SSG (define-derived-mode astro-mode web-mode "astro") (use-package web-mode :config (setopt web-mode-attribute-indent-offset 2 web-mode-code-indent-offset 2 web-mode-css-indent-offset 2 web-mode-markup-indent-offset 2 web-mode-enable-front-matter-block t web-mode-engines-alist '(("blade" . "\\.blade\\.") ("razor" . "\\.cshtml\\'"))) :mode ("\\.astro\\'" . astro-mode) ("\\.\\(cshtml\\|html?\\|razor\\)\\'" . web-mode)) ;; Show keybind hints (use-package which-key :config (setopt which-key-idle-delay 0.3 ;; which-key-sort-order 'which-key-description-order which-key-side-window-max-width 0.33) (which-key-mode) :defer 1) ;; Expand abbreviations to templates (use-package yasnippet :bind (;; :map yas-minor-mode-map ; need to enable mode with something other than `:commands' ("C-c y i" . yas-insert-snippet) ("C-c y v" . yas-visit-snippet-file)) :config (yas-reload-all) ; needed if not using `yas-global-mode' (yas-minor-mode)) (use-package yasnippet-snippets :after (yasnippet)) (use-package consult-yasnippet :after (consult yasnippet) :bind (:map yas-minor-mode-map ("C-c y c" . consult-yasnippet))) ;; YAML (use-package yaml-pro :hook (yaml-ts-mode . yaml-pro-ts-mode)) ;; Zig (use-package reformatter ; required for `zig-format' functions :after (zig-mode)) (use-package zig-mode :commands (zig-mode) :config (defun zig-compile () "Save buffer and compile using `zig build`. Overrides `zig-compile' in 'zig-mode.el'." (interactive) (save-buffer) (zig--run-cmd "build")) (setopt fill-column 100 zig-format-on-save nil)) (add-to-list 'load-path "~/.config/emacs/lisp/") (require 'init-org) ;;; init.el ends here