From 6949451b00eccbf03a6e070afafca85bec5daac5 Mon Sep 17 00:00:00 2001 From: Luigi Sartor Piucco Date: Sat, 24 Sep 2022 17:33:32 -0300 Subject: [PATCH 01/25] module: add :completion corfu This commit's primary goal is allowing use of [Corfu](https://github.com/minad/corfu) as an alternative to [Company](https://github.com/company-mode/company-mode). It introduces a module under `:completion` for this purpose, plus some conditionals on other relevant modules to toggle functionality like lsp back-ends and [Cape](https://github.com/minad/cape) capfs for certain modes. Other optional or miscellaneous features include: - Corfu is enabled in the minibuffer if `completion-at-point` is bound; - Support for displaying the completion's documentation on a secondary popup; - Support for terminal display if :os tty; - Support for icons if +icons; --- modules/completion/corfu/README.org | 229 ++++++++++++++++++++++ modules/completion/corfu/autoload.el | 10 + modules/completion/corfu/config.el | 92 +++++++++ modules/completion/corfu/packages.el | 11 ++ modules/config/default/+emacs-bindings.el | 2 +- modules/config/default/+evil-bindings.el | 47 ++++- modules/config/default/config.el | 33 ++++ modules/tools/lsp/+lsp.el | 5 +- templates/init.example.el | 1 + 9 files changed, 422 insertions(+), 8 deletions(-) create mode 100644 modules/completion/corfu/README.org create mode 100644 modules/completion/corfu/autoload.el create mode 100644 modules/completion/corfu/config.el create mode 100644 modules/completion/corfu/packages.el diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org new file mode 100644 index 000000000..82b910f43 --- /dev/null +++ b/modules/completion/corfu/README.org @@ -0,0 +1,229 @@ +#+title: :completion corfu +#+subtitle: Complete with cap(f), cape and a flying feather +#+created: September 9, 2022 +#+since: 3.0.0 (#7002) + +* Description :unfold: +This module provides code completion, powered by [[doom-package:corfu]]. + +It is recommended to enable either this or [[doom-module::completion company]] in +case you desire pre-configured auto-completion. Corfu is much lighter weight and +focused, plus it's built on native Emacs functionality, whereas Company is heavy +and highly non-native, but has some extra features and more maturity. + +If you choose Corfu, we also highly recomend reading [[https://github.com/minad/corfu][its README]] and [[https://github.com/minad/cape][cape's +README]], as the backend is very configurable and provides many power-user +utilities for fine-tuning. Only some of common behaviors are documented here. + +** Maintainers +- [[doom-user:][@LuigiPiucco]] + +[[doom-contrib-maintainer:][Become a maintainer?]] + +** Module flags +- +icons :: + Display icons beside completion suggestions. +- +orderless :: + Pull in [[doom-package:orderless]] if necessary and apply multi-component + completion (still needed if [[doom-module::completion vertico]] is active). + +** Packages +- [[doom-package:corfu]] +- [[doom-package:cape]] +- [[doom-package:nerd-icons-corfu]] if [[doom-module::completion corfu +icons]] +- [[doom-package:orderless]] if [[doom-module::completion corfu +orderless]] +- [[doom-package:corfu-terminal]] if [[doom-module::os tty]] + +** Hacks +/No hacks documented for this module./ + +** TODO Changelog +# This section will be machine generated. Don't edit it by hand. +/This module does not have a changelog yet./ + +* Installation +Enable this module in your ~doom!~ block. + +This module has no direct requirements, but some languages may have their own +requirements to fulfill before you get code completion in them (and some +languages may lack code completion support altogether). Run ~$ doom doctor~ to +find out if you're missing any dependencies. Note that Corfu may have support +for completions in languages that have no development intelligence, since it +supports generic, context insensitive candidates such as file names or recurring +words. + +* TODO Usage +#+begin_quote + 🔨 /This module's usage documentation is incomplete./ [[doom-contrib-module:][Complete it?]] +#+end_quote + +By default, completion gets triggered after typing 2 non-space consecutive +characters, by means of [[kbd:][C-SPC]] at any moment or [[kbd:][TAB]] on a line with proper +indentation. Many styles of completion are documented below, which can be +composed to suit the user. The following keybindings are generally available: + +| Keybind | Description | +|---------+--------------------------------------------| +| [[kbd:][C-n]] | Go to next candidate | +| [[kbd:][C-p]] | Go to previous candidate | +| [[kbd:][C-S-n]] | Go to next doc line | +| [[kbd:][C-S-p]] | Go to previous doc line | +| [[kbd:][C-S-s]] | Export to minibuffer | +| [[kbd:][TAB]] | (when not completing) Indent or complete | +| [[kbd:][C-SPC]] | (when not completing) Complete | +| [[kbd:][C-u]] | (evil) Go to next candidate page | +| [[kbd:][C-d]] | (evil) Go to previous candidate page | +| [[kbd:][C-h]] | (evil) Toggle documentation (if available) | +| [[kbd:][M-t]] | (emacs) (when not completing) Complete | + +Bindings in the following sections are additive, and unless otherwise noted, are +enabled by default with configurable behavior. Additionally, for users of evil, +[[kdb:][C-SPC]] is smart regarding your state. In normal-like states, enter insert then +start corfu; in visual-like states, perform [[help:evil-change][evil-change]] (which leaves you in +insert state) then start corfu; in insert-like states, start corfu immediatelly. + +** Commit preview on type +When the completion popup is visible, by default the current candidate is +previewed into the buffer, and further input commits that candidate as previewed +(note it does not perform candidate exit actions, such as expanding snippets). + +The feature is in line with other common editors, but if you prefer the preview +to be only visual or for there to be no preview, configure +[[var:corfu-preview-current]]. + +#+begin_src emacs-lisp +;; Non-inserting preview +(setq corfu-preview-current t) +;; No preview +(setq corfu-preview-current nil) +#+end_src + +** Commit on [[kbd:][RET]] with pass-through +A lot of people like to use [[kbd:][RET]] to commit, so here we bind it to Corfu's +insertion function. Note that Corfu allows "no candidate" to be selected, and in +that case, we have a custom binding to quit completion and pass-through. To make +it less obtrusive by default, the popup starts in this unselected state. See +[[var:corfu-preselect]] to alter the initial behavior; it can start with the first +one selected, for instance. Then, you have to move one candidate backwards to +pass-through The exact action of [[kbd:][RET]] can be changed via +[[var:+corfu-want-ret-to-confirm]]. + +| Keybind | Description | +|---------+-----------------------| +| [[kbd:][RET]] | Insert candidate DWIM | + +** Cycle directionally +If you'd rather think in directions rather than next/previous, arrow keys and vi +movements to control the selection and documentation view are bound by default. +You may unbind them by setting to nil, see ~map!~'s documentation. + +| Keybind | Description | +|----------+---------------------------------| +| [[kbd:][]] | Go to next candidate | +| [[kbd:][]] | Go to previous candidate | +| [[kbd:][C-j]] | (evil) Go to next candidate | +| [[kbd:][C-k]] | (evil) Go to previous candidate | +| [[kbd:][C-]] | Go to next doc line | +| [[kbd:][C-]] | Go to previous doc line | +| [[kbd:][C-S-j]] | (evil) Go to next doc line | +| [[kbd:][C-S-k]] | (evil) Go to previous doc line | + +** Cycle with [[kbd:][TAB]] +[[kbd:][TAB]]-based cycling alternatives are also bound according to the table below: + +| Keybind | Description | +|---------+--------------------------| +| [[kbd:][TAB]] | Go to next candidate | +| [[kbd:][S-TAB]] | Go to previous candidate | + +** Searching with multiple keywords (~+orderless~) +If the [[doom-module::completion corfu +orderless]] flag is enabled, users can +perform code completion with multiple search keywords by use of space as the +separator. More information can be found [[https://github.com/oantolin/orderless#company][here]]. Pressing [[kdb:][C-SPC]] again while +completing inserts a space as separator. This allows searching with +space-separated terms; each piece will match individually and in any order, with +smart casing. Pressing just [[kbd:][SPC]] acts as normal and quits completion, so that +when typing sentences it doesn't try to complete the whole sentence instead of +just the word. + +| Keybind | Description | +|---------+-------------------------------------------------| +| [[kbd:][C-SPC]] | (evil) (when completing) Insert separator DWIM | +| [[kbd:][M-SPC]] | (emacs) (when completing) Insert separator DWIM | +| [[kbd:][SPC]] | (when completing) Quit autocompletion | +| [[kbd:][SPC]] | (when completing with separators) Self-insert | + +** Exporting to the minibuffer (requires [[doom-module::completion vertico]]) +When using the [[doom-module::completion vertico]] module, which pulls in the +[[doom-package:consult]] package, the entries shown in the completion popup can be +exported to a consult minibuffer, giving access to all the manipulations the +Vertico suite allows. For instance, one could use this to export with +[[doom-package:embark]] via [[kbd:][C-c C-l]] and get a buffer with all candidates. + +* Configuration +A few variables may be set to change behavior of this module: + +- [[var:completion-at-point-functions]] :: + This is not a module/package variable, but a builtin Emacs one. Even so, it's + very important to how Corfu works, so we document it here. It contains a list + of functions that are called in turn to generate completion candidates. The + regular (non-lexical) value should contain few entries and they should + generally be context aware, so as to predict what you need. Additional + functions can be added as you get into more and more specific contexts. Also, + there may be cases where you know beforehand the kind of candidate needed, and + want to enable only that one. For this, the variable may be lexically bound to + the correct value, or you may call the CAPF interactively if a single function + is all you need. +- [[var:corfu-auto-delay]] :: + Number of seconds till completion occurs automatically. Defaults to 0.1. +- [[var:corfu-auto-prefix]] :: + Number of characters till auto-completion starts to happen. Defaults to 2. +- [[var:corfu-on-exact-match]] :: + Configures behavior for exact matches. +- [[var:corfu-preselect]] :: + Configures startup selection, choosing between the first candidate or the + prompt. +- [[var:corfu-preview-current]] :: + Configures current candidate preview. +- [[var:+corfu-want-ret-to-confirm]] :: + Enables commiting with [[RET]] when the popup is visible. Default is ~t~, may be set to + ~'minibuffer~ if you want to commit both the completion and the minibuffer when + active. When ~nil~, it is always passed-through. + +** Adding CAPFs to a mode +To add other CAPFs on a mode-per-mode basis, put either of the following in your +~config.el~: + +#+begin_src emacs-lisp +(add-hook! some-mode (add-to-list 'completion-at-point-functions #'some-capf)) +;; OR, but note the different call signature +(add-hook 'some-mode-hook (lambda () (add-to-list 'completion-at-point-functions #'some-capf))) +#+end_src + +~DEPTH~ above is an integer between -100, 100, and defaults to 0 if nil. Also +see ~add-hook!~'s documentation for additional ways to call it. ~add-hook~ only +accepts the quoted arguments form above. + +** Adding CAPFs to a key +To add other CAPFs to keys, adapt the snippet below into your ~config.el~: + +#+begin_src emacs-lisp +(map! :map some-mode-map + "C-x e" #'cape-emoji) +#+end_src + +It's okay to add to the mode directly because ~completion-at-point~ works +regardless of Corfu (the latter is an enhanced UI for the former). Just note not +all CAPFs are interactive to be called this way, in which case you can use +[[doom-package:cape]]'s adapter to enable this. + +* Troubleshooting +[[doom-report:][Report an issue?]] + +* Frequently asked questions +/This module has no FAQs yet./ [[doom-suggest-faq:][Ask one?]] + +* TODO Appendix +#+begin_quote + 🔨 This module has no appendix yet. [[doom-contrib-module:][Write one?]] +#+end_quote diff --git a/modules/completion/corfu/autoload.el b/modules/completion/corfu/autoload.el new file mode 100644 index 000000000..c34cb8232 --- /dev/null +++ b/modules/completion/corfu/autoload.el @@ -0,0 +1,10 @@ +;;; completion/corfu/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun +corfu-move-to-minibuffer () + ;; Taken from corfu's README. + ;; TODO: extend this to other completion front-ends. + (interactive) + (let ((completion-extra-properties corfu--extra) + (completion-cycle-threshold completion-cycling)) + (apply #'consult-completion-in-region completion-in-region--data))) diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el new file mode 100644 index 000000000..997af183d --- /dev/null +++ b/modules/completion/corfu/config.el @@ -0,0 +1,92 @@ +;;; completion/corfu/config.el -*- lexical-binding: t; -*- + +(defvar +corfu-want-ret-to-confirm t + "Configure how the user expects RET to behave. +Possible values are: +- t (default): Insert candidate if one is selected, pass-through otherwise; +- `minibuffer': Insert candidate if one is selected, pass-through otherwise, + and immediatelly exit if in the minibuffer; +- nil: Pass-through without inserting.") + +;; +;;; Packages +(use-package! corfu + :hook (doom-first-input . global-corfu-mode) + :init + (add-hook! 'minibuffer-setup-hook + (defun +corfu-enable-in-minibuffer () + "Enable Corfu in the minibuffer if `completion-at-point' is bound." + (when (where-is-internal #'completion-at-point (list (current-local-map))) + (setq-local corfu-echo-delay nil) + (corfu-mode +1)))) + :config + (setq corfu-auto t + corfu-auto-delay 0.1 + corfu-auto-prefix 2 + global-corfu-modes '((not + erc-mode + circe-mode + help-mode + gud-mode + vterm-mode) + t) + corfu-cycle t + corfu-separator (when (modulep! +orderless) ?\s) + corfu-preselect 'prompt + corfu-count 16 + corfu-max-width 120 + corfu-preview-current 'insert + corfu-on-exact-match nil + corfu-quit-at-boundary (if (modulep! +orderless) 'separator t) + corfu-quit-no-match (if (modulep! +orderless) 'separator t) + tab-always-indent 'complete) + (add-to-list 'completion-category-overrides `(lsp-capf (styles ,@completion-styles))) + (add-to-list 'corfu-auto-commands #'lispy-colon) + (add-to-list 'corfu-continue-commands #'+corfu-move-to-minibuffer) + (add-hook 'evil-insert-state-exit-hook #'corfu-quit)) + +(use-package! cape + :defer t + :init + (add-hook! prog-mode + (defun +corfu-add-cape-file-h () + (add-to-list 'completion-at-point-functions #'cape-file))) + (add-hook! (org-mode markdown-mode) + (defun +corfu-add-cape-elisp-block-h () + (add-hook 'completion-at-point-functions #'cape-elisp-block 0 t))) + + ;; Make these capfs composable. + (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible) + (advice-add #'lsp-completion-at-point :around #'cape-wrap-nonexclusive) + (advice-add #'comint-completion-at-point :around #'cape-wrap-nonexclusive) + (advice-add #'eglot-completion-at-point :around #'cape-wrap-nonexclusive) + (advice-add #'pcomplete-completions-at-point :around #'cape-wrap-nonexclusive) + ;; From the `cape' readme. Without this, Eshell autocompletion is broken on + ;; Emacs28. + (when (< emacs-major-version 29) + (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) + (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify))) + +(use-package! corfu-terminal + :when (not (display-graphic-p)) + :hook ((corfu-mode . corfu-terminal-mode))) + +;; +;;; Extensions + +(use-package! corfu-history + :hook ((corfu-mode . corfu-history-mode)) + :config + (after! savehist (add-to-list 'savehist-additional-variables 'corfu-history))) + +(use-package! corfu-popupinfo + :hook ((corfu-mode . corfu-popupinfo-mode)) + :config + (setq corfu-popupinfo-delay '(0.5 . 1.0))) + +(use-package! nerd-icons-corfu + :when (modulep! +icons) + :defer t + :init + (after! corfu + (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))) diff --git a/modules/completion/corfu/packages.el b/modules/completion/corfu/packages.el new file mode 100644 index 000000000..fba829248 --- /dev/null +++ b/modules/completion/corfu/packages.el @@ -0,0 +1,11 @@ +;; -*- no-byte-compile: t; -*- +;;; completion/corfu/packages.el + +(package! corfu :pin "c1e7b6190b00158e67347b4db0a8f7964e5d2f8b") +(package! cape :pin "a397a0c92de38277b7f835fa999fac400a764908") +(when (modulep! +icons) + (package! nerd-icons-corfu :pin "7077bb76fefc15aed967476406a19dc5c2500b3c")) +(when (modulep! +orderless) + (package! orderless :pin "b24748093b00b37c3a572c4909f61c08fa27504f")) +(when (modulep! :os tty) + (package! corfu-terminal :pin "501548c3d51f926c687e8cd838c5865ec45d03cc")) diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index 7aa4dfc41..7c63e68e2 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -511,7 +511,7 @@ "C-x C-b" #'ibuffer "C-x K" #'doom/kill-this-buffer-in-all-windows - ;;; company-mode + ;;; completion (in-buffer) (:when (modulep! :completion company) "C-;" #'+company/complete (:after company diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index fd2487410..b07109ebe 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -43,7 +43,10 @@ #'yas-expand (and (bound-and-true-p company-mode) (modulep! :completion company +tng)) - #'company-indent-or-complete-common) + #'company-indent-or-complete-common + (and (bound-and-true-p corfu-mode) + (modulep! :completion corfu)) + #'completion-at-point) :m [tab] (cmds! (and (modulep! :editor snippets) (evil-visual-state-p) (or (eq evil-visual-selection 'line) @@ -127,7 +130,7 @@ ;; ;;; Module keybinds -;;; :completion +;;; :completion (in-buffer) (map! (:when (modulep! :completion company) :i "C-@" (cmds! (not (minibufferp)) #'company-complete-common) :i "C-SPC" (cmds! (not (minibufferp)) #'company-complete-common) @@ -156,7 +159,38 @@ "C-s" #'company-filter-candidates [escape] #'company-search-abort))) - (:when (modulep! :completion ivy) + (:when (modulep! :completion corfu) + (:after corfu + (:map corfu-mode-map + :i "C-SPC" #'completion-at-point + :n "C-SPC" (cmd! (call-interactively #'evil-insert-state) + (call-interactively #'completion-at-point)) + :v "C-SPC" (cmd! (call-interactively #'evil-change) + (call-interactively #'completion-at-point))) + (:map corfu-map + :i "C-SPC" #'corfu-insert-separator + "C-k" #'corfu-previous + "C-j" #'corfu-next + "C-u" (cmd! (let (corfu-cycle) + (funcall-interactively #'corfu-next (- corfu-count)))) + "C-d" (cmd! (let (corfu-cycle) + (funcall-interactively #'corfu-next corfu-count))))) + (:after corfu-popupinfo + :map corfu-popupinfo-map + "C-h" #'corfu-popupinfo-toggle + ;; Reversed because popupinfo assumes opposite of what feels intuitive + ;; with evil. + "C-S-k" #'corfu-popupinfo-scroll-down + "C-S-j" #'corfu-popupinfo-scroll-up + "C-" #'corfu-popupinfo-scroll-down + "C-" #'corfu-popupinfo-scroll-up + "C-S-p" #'corfu-popupinfo-scroll-down + "C-S-n" #'corfu-popupinfo-scroll-up + "C-S-u" (cmd!! #'corfu-popupinfo-scroll-down nil corfu-popupinfo-min-height) + "C-S-d" (cmd!! #'corfu-popupinfo-scroll-up nil corfu-popupinfo-min-height)))) + +;;; :completion (separate) +(map! (:when (modulep! :completion ivy) (:after ivy :map ivy-minibuffer-map "C-SPC" #'ivy-call-and-recenter ; preview file @@ -169,7 +203,8 @@ [C-return] #'+ivy/git-grep-other-window-action)) (:when (modulep! :completion helm) - (:after helm :map helm-map + (:after helm + :map helm-map [remap next-line] #'helm-next-line [remap previous-line] #'helm-previous-line [left] #'left-char @@ -387,9 +422,9 @@ :desc "Incoming call hierarchy" "y" #'lsp-treemacs-call-hierarchy :desc "Outgoing call hierarchy" "Y" (cmd!! #'lsp-treemacs-call-hierarchy t) :desc "References tree" "R" (cmd!! #'lsp-treemacs-references t) - :desc "Symbols" "S" #'lsp-treemacs-symbols) + :desc "Symbols" "S" #'lsp-treemacs-symbols :desc "LSP" "l" #'+default/lsp-command-map - :desc "LSP Rename" "r" #'lsp-rename) + :desc "LSP Rename" "r" #'lsp-rename)) (:when (modulep! :tools lsp +eglot) :desc "LSP Execute code action" "a" #'eglot-code-actions :desc "LSP Rename" "r" #'eglot-rename diff --git a/modules/config/default/config.el b/modules/config/default/config.el index e67adf1e3..8316284d8 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -458,6 +458,39 @@ Continues comments if executed from a commented line. Consults '(evil-ex-completion-map))) "C-s" command)) + (map! :when (modulep! :completion corfu) + :after corfu + (:map corfu-map + "C-S-s" #'+corfu-move-to-minibuffer + "C-p" #'corfu-previous + "C-n" #'corfu-next + "S-TAB" #'corfu-previous + [backtab] #'corfu-previous + "TAB" #'corfu-next + [tab] #'corfu-next)) + (let ((cmds-ret + `(menu-item "Insert completion DWIM" corfu-insert + :filter ,(lambda (cmd) + (interactive) + (cond ((null +corfu-want-ret-to-confirm) + (corfu-quit) + nil) + ((eq +corfu-want-ret-to-confirm 'minibuffer) + (funcall-interactively cmd) + nil) + ((and (or (not (minibufferp nil t)) + (eq +corfu-want-ret-to-confirm t)) + (>= corfu--index 0)) + cmd) + ((or (not (minibufferp nil t)) + (eq +corfu-want-ret-to-confirm t)) + nil) + (t cmd)))))) + (map! :when (modulep! :completion corfu) + :map corfu-map + :gi [return] cmds-ret + :gi "RET" cmds-ret)) + ;; Smarter C-a/C-e for both Emacs and Evil. C-a will jump to indentation. ;; Pressing it again will send you to the true bol. Same goes for C-e, except ;; it will ignore comments+trailing whitespace before jumping to eol. diff --git a/modules/tools/lsp/+lsp.el b/modules/tools/lsp/+lsp.el index 5ae4a417d..96361aae4 100644 --- a/modules/tools/lsp/+lsp.el +++ b/modules/tools/lsp/+lsp.el @@ -138,8 +138,11 @@ server getting expensively restarted when reverting buffers." " ")) (add-to-list 'global-mode-string '(t (:eval lsp-modeline-icon)) - 'append)))))) + 'append))))) + (when (modulep! :completion corfu) + (setq lsp-completion-provider :none) + (add-hook 'lsp-mode-hook #'lsp-completion-mode))) (use-package! lsp-ui :hook (lsp-mode . lsp-ui-mode) diff --git a/templates/init.example.el b/templates/init.example.el index 7a5e4e6c7..cef45ecb3 100644 --- a/templates/init.example.el +++ b/templates/init.example.el @@ -22,6 +22,7 @@ :completion company ; the ultimate code completion backend + ;;(corfu +orderless) ; complete with cap(f), cape and a flying feather! ;;helm ; the *other* search engine for love and life ;;ido ; the other *other* search engine... ;;ivy ; a search engine for love and life From 968a8975308c772d0298021884758792eab641e8 Mon Sep 17 00:00:00 2001 From: Luigi Sartor Piucco Date: Wed, 26 Jul 2023 19:57:21 -0300 Subject: [PATCH 02/25] feat(corfu): add snippets Yasnippet is now properly integrated! A previosly-unset default has now been given to `corfu-on-exact-match`. With snippets, it causes immediate expansion upon single match by default, so we set it to nil and recommend against changing it in the README. --- modules/completion/corfu/README.org | 3 ++- modules/completion/corfu/config.el | 8 ++++++++ modules/completion/corfu/packages.el | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index 82b910f43..74c8043d5 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -33,6 +33,7 @@ utilities for fine-tuning. Only some of common behaviors are documented here. - [[doom-package:nerd-icons-corfu]] if [[doom-module::completion corfu +icons]] - [[doom-package:orderless]] if [[doom-module::completion corfu +orderless]] - [[doom-package:corfu-terminal]] if [[doom-module::os tty]] +- [[doom-package:yasnippet-capf]] if [[doom-module::editor snippets]] ** Hacks /No hacks documented for this module./ @@ -50,7 +51,7 @@ languages may lack code completion support altogether). Run ~$ doom doctor~ to find out if you're missing any dependencies. Note that Corfu may have support for completions in languages that have no development intelligence, since it supports generic, context insensitive candidates such as file names or recurring -words. +words. Snippets may also appear in the candidate list if available. * TODO Usage #+begin_quote diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index 997af183d..6ad3fe349 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -67,6 +67,14 @@ Possible values are: (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent) (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify))) +(use-package! yasnippet-capf + :when (modulep! :editor snippets) + :defer t + :init + (add-hook! 'yas-minor-mode-hook + (defun +corfu-add-yasnippet-capf-h () + (add-hook 'completion-at-point-functions #'yasnippet-capf 30 t)))) + (use-package! corfu-terminal :when (not (display-graphic-p)) :hook ((corfu-mode . corfu-terminal-mode))) diff --git a/modules/completion/corfu/packages.el b/modules/completion/corfu/packages.el index fba829248..cf45a992f 100644 --- a/modules/completion/corfu/packages.el +++ b/modules/completion/corfu/packages.el @@ -9,3 +9,5 @@ (package! orderless :pin "b24748093b00b37c3a572c4909f61c08fa27504f")) (when (modulep! :os tty) (package! corfu-terminal :pin "501548c3d51f926c687e8cd838c5865ec45d03cc")) +(when (modulep! :editor snippets) + (package! yasnippet-capf :pin "9043f8275176a8f198ce8e81fadab1870fa165bb")) From 365a95de76e14939b7296c636192319a675844cf Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Mon, 30 Oct 2023 14:36:19 -0400 Subject: [PATCH 03/25] feat(corfu): more CAPFs and ergonomy changes Add CAPFs from cape: - `cape-dabbrev`; - `cape-elisp-block`; - `cape-file`; Fix some CAPFs via cape: - Make non-exclusive, purified and silent `pcomplete-completions-at-point`; - Make non-exclusive and non-interruptable `lsp-completion-at-point`; - Make non-exclusive `eglot-completion-at-point`; - Make non-exclusive `comint-completion-at-point`; Fix and improve keybindings: - Smart `DEL`; Add depth to CAPFs, allowing ordering to be adjustable. --- modules/completion/corfu/README.org | 21 +++++++++++++++++++-- modules/completion/corfu/config.el | 22 +++++++++++++++++++++- modules/config/default/config.el | 10 +++++++++- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index 74c8043d5..252423567 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -26,6 +26,9 @@ utilities for fine-tuning. Only some of common behaviors are documented here. - +orderless :: Pull in [[doom-package:orderless]] if necessary and apply multi-component completion (still needed if [[doom-module::completion vertico]] is active). +- +dabbrev :: + Enable and configure [[doom-package:dabbrev]] as a close-to-universal CAPF + fallback. ** Packages - [[doom-package:corfu]] @@ -190,15 +193,18 @@ A few variables may be set to change behavior of this module: Enables commiting with [[RET]] when the popup is visible. Default is ~t~, may be set to ~'minibuffer~ if you want to commit both the completion and the minibuffer when active. When ~nil~, it is always passed-through. +- [[var:+corfu-buffer-scanning-size-limit]] :: + Sets the maximum buffer size to be scanned by ~cape-dabbrev~. Defaults to 1 MB. + Set this if you are having performance problems using the CAPF. ** Adding CAPFs to a mode To add other CAPFs on a mode-per-mode basis, put either of the following in your ~config.el~: #+begin_src emacs-lisp -(add-hook! some-mode (add-to-list 'completion-at-point-functions #'some-capf)) +(add-hook! some-mode (add-hook 'completion-at-point-functions #'some-capf depth t)) ;; OR, but note the different call signature -(add-hook 'some-mode-hook (lambda () (add-to-list 'completion-at-point-functions #'some-capf))) +(add-hook 'some-mode-hook (lambda () (add-hook 'completion-at-point-functions #'some-capf depth t))) #+end_src ~DEPTH~ above is an integer between -100, 100, and defaults to 0 if nil. Also @@ -221,6 +227,17 @@ all CAPFs are interactive to be called this way, in which case you can use * Troubleshooting [[doom-report:][Report an issue?]] +If you have performance issues with ~cape-dabbrev~, the first thing I recommend +doing is to look at the list of buffers Dabbrev is scanning: + +#+begin_src emacs-lisp +(dabbrev--select-buffers) ; => (# #> # ...) +(length (dabbrev--select-buffers)) ; => 37 +#+end_src + +... and modify ~dabbrev-ignored-buffer-regexps~ or ~dabbrev-ignored-buffer-modes~ +accordingly. + * Frequently asked questions /This module has no FAQs yet./ [[doom-suggest-faq:][Ask one?]] diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index 6ad3fe349..ae2e8409a 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -8,6 +8,9 @@ Possible values are: and immediatelly exit if in the minibuffer; - nil: Pass-through without inserting.") +(defvar +corfu-buffer-scanning-size-limit (* 1 1024 1024) ; 1 MB + "Size limit for a buffer to be scanned by `cape-dabbrev'.") + ;; ;;; Packages (use-package! corfu @@ -50,10 +53,27 @@ Possible values are: :init (add-hook! prog-mode (defun +corfu-add-cape-file-h () - (add-to-list 'completion-at-point-functions #'cape-file))) + (add-hook 'completion-at-point-functions #'cape-file -10 t))) (add-hook! (org-mode markdown-mode) (defun +corfu-add-cape-elisp-block-h () (add-hook 'completion-at-point-functions #'cape-elisp-block 0 t))) + ;; Enable Dabbrev completion basically everywhere as a fallback. + (when (modulep! +dabbrev) + (setq cape-dabbrev-check-other-buffers t) + ;; Set up `cape-dabbrev' options. + (defun +dabbrev-friend-buffer-p (other-buffer) + (< (buffer-size other-buffer) +corfu-buffer-scanning-size-limit)) + (add-hook! (prog-mode text-mode conf-mode comint-mode minibuffer-setup + eshell-mode) + (defun +corfu-add-cape-dabbrev-h () + (add-hook 'completion-at-point-functions #'cape-dabbrev 20 t))) + (after! dabbrev + (setq dabbrev-friend-buffer-function #'+dabbrev-friend-buffer-p + dabbrev-ignored-buffer-regexps + '("^ " + "\\(TAGS\\|tags\\|ETAGS\\|etags\\|GTAGS\\|GRTAGS\\|GPATH\\)\\(<[0-9]+>\\)?") + dabbrev-upcase-means-case-search t) + (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode))) ;; Make these capfs composable. (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible) diff --git a/modules/config/default/config.el b/modules/config/default/config.el index 8316284d8..e3becc12a 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -468,7 +468,13 @@ Continues comments if executed from a commented line. Consults [backtab] #'corfu-previous "TAB" #'corfu-next [tab] #'corfu-next)) - (let ((cmds-ret + (let ((cmds-del + `(menu-item "Reset completion" corfu-reset + :filter ,(lambda (cmd) + (when (and (>= corfu--index 0) + (eq corfu-preview-current 'insert)) + cmd)))) + (cmds-ret `(menu-item "Insert completion DWIM" corfu-insert :filter ,(lambda (cmd) (interactive) @@ -488,6 +494,8 @@ Continues comments if executed from a commented line. Consults (t cmd)))))) (map! :when (modulep! :completion corfu) :map corfu-map + [backspace] cmds-del + "DEL" cmds-del :gi [return] cmds-ret :gi "RET" cmds-ret)) From 0588b42b46265450128e438cef82f7906d8722e5 Mon Sep 17 00:00:00 2001 From: Luigi Sartor Piucco Date: Sun, 29 Oct 2023 14:31:13 -0300 Subject: [PATCH 04/25] feat(corfu,vertico): use equal orderless config This removes the old `&` separator for Vertico (does anyone use that instead of just space?) in favor of escapable space and unifies orderless config with Corfu. Also implements smart separator insert/escape/reset on `C-SPC` Co-authored-by: Liam Hupfer --- modules/completion/corfu/README.org | 5 ++++- modules/completion/corfu/autoload.el | 12 ++++++++++++ modules/completion/corfu/config.el | 13 +++++++++++++ modules/completion/vertico/config.el | 2 +- modules/config/default/config.el | 1 + 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index 252423567..9f0f7093c 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -148,7 +148,10 @@ completing inserts a space as separator. This allows searching with space-separated terms; each piece will match individually and in any order, with smart casing. Pressing just [[kbd:][SPC]] acts as normal and quits completion, so that when typing sentences it doesn't try to complete the whole sentence instead of -just the word. +just the word. Pressing [[kdb:][C-SPC]] with point after a separator escapes it with a +backslash, including the space in the search term, and pressing it with an +already escaped separator before point deletes it. Thus, you can cycle back if +you accidentaly press more than needed. | Keybind | Description | |---------+-------------------------------------------------| diff --git a/modules/completion/corfu/autoload.el b/modules/completion/corfu/autoload.el index c34cb8232..5003d3b7c 100644 --- a/modules/completion/corfu/autoload.el +++ b/modules/completion/corfu/autoload.el @@ -8,3 +8,15 @@ (let ((completion-extra-properties corfu--extra) (completion-cycle-threshold completion-cycling)) (apply #'consult-completion-in-region completion-in-region--data))) + +;;;###autoload +(defun +corfu-smart-sep-toggle-escape () + "Insert `corfu-separator' or toggle escape if it's already there." + (interactive) + (cond ((and (char-equal (char-before) corfu-separator) + (char-equal (char-before (1- (point))) ?\\)) + (save-excursion (delete-char -2))) + ((char-equal (char-before) corfu-separator) + (save-excursion (backward-char 1) + (insert-char ?\\))) + (t (call-interactively #'corfu-insert-separator)))) diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index ae2e8409a..bfa302546 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -46,6 +46,7 @@ Possible values are: (add-to-list 'completion-category-overrides `(lsp-capf (styles ,@completion-styles))) (add-to-list 'corfu-auto-commands #'lispy-colon) (add-to-list 'corfu-continue-commands #'+corfu-move-to-minibuffer) + (add-to-list 'corfu-continue-commands #'+corfu-smart-sep-toggle-escape) (add-hook 'evil-insert-state-exit-hook #'corfu-quit)) (use-package! cape @@ -118,3 +119,15 @@ Possible values are: :init (after! corfu (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))) + +;; If vertico is not enabled, orderless will be installed but not configured. +;; That may break smart separator behavior, so we conditionally configure it. +(use-package! orderless + :when (and (not (modulep! :completion vertico)) + (modulep! +orderless)) + :config + (setq completion-styles '(orderless basic) + completion-category-defaults nil + completion-category-overrides '((file (styles orderless partial-completion))) + orderless-component-separator #'orderless-escapable-split-on-space) + (set-face-attribute 'completions-first-difference nil :inherit nil)) diff --git a/modules/completion/vertico/config.el b/modules/completion/vertico/config.el index 0d52ab950..3f2bf3f30 100644 --- a/modules/completion/vertico/config.el +++ b/modules/completion/vertico/config.el @@ -107,7 +107,7 @@ orderless." ;; find-file etc. completion-category-overrides '((file (styles +vertico-basic-remote orderless partial-completion))) orderless-style-dispatchers '(+vertico-orderless-dispatch) - orderless-component-separator "[ &]") + orderless-component-separator #'orderless-escapable-split-on-space) ;; ...otherwise find-file gets different highlighting than other commands (set-face-attribute 'completions-first-difference nil :inherit nil)) diff --git a/modules/config/default/config.el b/modules/config/default/config.el index e3becc12a..21aa7268d 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -461,6 +461,7 @@ Continues comments if executed from a commented line. Consults (map! :when (modulep! :completion corfu) :after corfu (:map corfu-map + [remap corfu-insert-separator] #'+corfu-smart-sep-toggle-escape "C-S-s" #'+corfu-move-to-minibuffer "C-p" #'corfu-previous "C-n" #'corfu-next From 763464afdb9c46d5f60d4565c110d6d1a05fbf99 Mon Sep 17 00:00:00 2001 From: Luigi Sartor Piucco Date: Sun, 4 Feb 2024 17:17:13 -0300 Subject: [PATCH 05/25] feat(corfu): general move-to-minibuffer impl We previously implemented only consult/vertico as a target for export, now we have all of them. It was necessary to use case-by-case conditions, unfortunately, because other UIs have subtle quirks that prevent a single generalized approach to work. Ivy is almost compliant, but it needs beg and end to not be markers. Helm doesn't replace `completion-in-region-function`, it expects to go around the default `completion--in-region`. It's supposed to add the advice by itself, but it's very unreliable, so we do the wrapping manually. Ido doesn't implement `completion-in-region` and its `completing-read` is retricted to a list of strings as table, so we use default `completion--in-region` with no bells or whistles. --- modules/completion/corfu/README.org | 11 +++++------ modules/completion/corfu/autoload.el | 27 ++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index 9f0f7093c..7413f53a1 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -160,12 +160,11 @@ you accidentaly press more than needed. | [[kbd:][SPC]] | (when completing) Quit autocompletion | | [[kbd:][SPC]] | (when completing with separators) Self-insert | -** Exporting to the minibuffer (requires [[doom-module::completion vertico]]) -When using the [[doom-module::completion vertico]] module, which pulls in the -[[doom-package:consult]] package, the entries shown in the completion popup can be -exported to a consult minibuffer, giving access to all the manipulations the -Vertico suite allows. For instance, one could use this to export with -[[doom-package:embark]] via [[kbd:][C-c C-l]] and get a buffer with all candidates. +** Exporting to the minibuffer +The entries shown in the completion popup can be exported to a ~completing-read~ +minibuffer, giving access to all the manipulations that suite allows. Using +Vertico for instance, one could use this to export with [[doom-package:embark]] via +[[kbd:][C-c C-l]] and get a buffer with all candidates. * Configuration A few variables may be set to change behavior of this module: diff --git a/modules/completion/corfu/autoload.el b/modules/completion/corfu/autoload.el index 5003d3b7c..43005c6c1 100644 --- a/modules/completion/corfu/autoload.el +++ b/modules/completion/corfu/autoload.el @@ -2,12 +2,29 @@ ;;;###autoload (defun +corfu-move-to-minibuffer () - ;; Taken from corfu's README. - ;; TODO: extend this to other completion front-ends. + "Move the current list of candidates to your choice of minibuffer completion UI." (interactive) - (let ((completion-extra-properties corfu--extra) - (completion-cycle-threshold completion-cycling)) - (apply #'consult-completion-in-region completion-in-region--data))) + (pcase completion-in-region--data + (`(,beg ,end ,table ,pred ,extras) + (let ((completion-extra-properties extras) + completion-cycle-threshold completion-cycling) + (cond ((and (modulep! :completion vertico) + (fboundp #'consult-completion-in-region)) + (consult-completion-in-region beg end table pred)) + ((and (modulep! :completion ivy) + (fboundp #'ivy-completion-in-region)) + (ivy-completion-in-region (marker-position beg) (marker-position end) table pred)) + ;; Important: `completion-in-region-function' is set to corfu at + ;; this moment, so `completion-in-region' (single -) doesn't work + ;; below. + ((modulep! :completion helm) + ;; Helm is special and wants to _wrap_ `completion--in-region' + ;; instead of replacing it in `completion-in-region-function'. + ;; But because the advice is too unreliable we "fake" the wrapping. + (helm--completion-in-region #'completion--in-region beg end table pred)) + ((modulep! :completion ido) + (completion--in-region beg end table pred)) + (t (error "No minibuffer completion UI available for moving to!"))))))) ;;;###autoload (defun +corfu-smart-sep-toggle-escape () From 4192c811130c2c2e73eeb83a9f24a9ba40f8b02f Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Wed, 28 Feb 2024 17:42:47 -0600 Subject: [PATCH 06/25] feat(corfu): make minibuffer completion optional --- modules/completion/corfu/README.org | 3 +++ modules/completion/corfu/config.el | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index 7413f53a1..48d591c91 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -198,6 +198,9 @@ A few variables may be set to change behavior of this module: - [[var:+corfu-buffer-scanning-size-limit]] :: Sets the maximum buffer size to be scanned by ~cape-dabbrev~. Defaults to 1 MB. Set this if you are having performance problems using the CAPF. +- [[var:+corfu-want-minibuffer-completion]] :: + Whether to enable Corfu in the minibuffer. See its documentation for + additional tweaks. ** Adding CAPFs to a mode To add other CAPFs on a mode-per-mode basis, put either of the following in your diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index bfa302546..8e9006765 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -11,6 +11,11 @@ Possible values are: (defvar +corfu-buffer-scanning-size-limit (* 1 1024 1024) ; 1 MB "Size limit for a buffer to be scanned by `cape-dabbrev'.") +(defvar +corfu-want-minibuffer-completion t + "Whether to enable Corfu in the minibuffer. +Setting this to `aggressive' will enable Corfu in more commands which +use the minibuffer such as `query-replace'.") + ;; ;;; Packages (use-package! corfu @@ -18,8 +23,20 @@ Possible values are: :init (add-hook! 'minibuffer-setup-hook (defun +corfu-enable-in-minibuffer () - "Enable Corfu in the minibuffer if `completion-at-point' is bound." - (when (where-is-internal #'completion-at-point (list (current-local-map))) + "Enable Corfu in the minibuffer." + (when (pcase +corfu-want-minibuffer-completion + ('aggressive + (not (or (bound-and-true-p mct--active) + (bound-and-true-p vertico--input) + (eq (current-local-map) read-passwd-map) + (and (featurep 'helm-core) (helm--alive-p)) + (and (featurep 'ido) (ido-active)) + (where-is-internal 'minibuffer-complete + (list (current-local-map))) + (memq #'ivy--queue-exhibit post-command-hook)))) + ('nil nil) + (_ (where-is-internal #'completion-at-point + (list (current-local-map))))) (setq-local corfu-echo-delay nil) (corfu-mode +1)))) :config From bfb9aabe27fbb89f9e3f31b306c0bc2a103f15fd Mon Sep 17 00:00:00 2001 From: StrawberryTea Date: Sat, 17 Feb 2024 10:47:35 -0600 Subject: [PATCH 07/25] feat(corfu): update minibuffer hints manually We need this advice to ensure that visual hints are updated before exiting. --- modules/completion/corfu/config.el | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index 8e9006765..defdc8aaa 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -64,7 +64,26 @@ use the minibuffer such as `query-replace'.") (add-to-list 'corfu-auto-commands #'lispy-colon) (add-to-list 'corfu-continue-commands #'+corfu-move-to-minibuffer) (add-to-list 'corfu-continue-commands #'+corfu-smart-sep-toggle-escape) - (add-hook 'evil-insert-state-exit-hook #'corfu-quit)) + (add-hook 'evil-insert-state-exit-hook #'corfu-quit) + + ;; If you want to update the visual hints after completing minibuffer commands + ;; with Corfu and exiting, you have to do it manually. + (defadvice! +corfu--insert-before-exit-minibuffer-a () + :before #'exit-minibuffer + (when (or (and (frame-live-p corfu--frame) + (frame-visible-p corfu--frame)) + (and (featurep 'corfu-terminal) + (popon-live-p corfu-terminal--popon))) + (when (member isearch-lazy-highlight-timer timer-idle-list) + (apply (timer--function isearch-lazy-highlight-timer) + (timer--args isearch-lazy-highlight-timer))) + (when (member (bound-and-true-p anzu--update-timer) timer-idle-list) + (apply (timer--function anzu--update-timer) + (timer--args anzu--update-timer))) + (when (member (bound-and-true-p evil--ex-search-update-timer) + timer-idle-list) + (apply (timer--function evil--ex-search-update-timer) + (timer--args evil--ex-search-update-timer)))))) (use-package! cape :defer t From f9c0243211a4654347c7bb83daf5e80416739227 Mon Sep 17 00:00:00 2001 From: Luigi Sartor Piucco Date: Mon, 4 Mar 2024 14:17:56 -0300 Subject: [PATCH 08/25] docs(corfu): add @LemonBreezes as co-maintainer Co-authored-by: StrawberryTea --- modules/completion/corfu/README.org | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/completion/corfu/README.org b/modules/completion/corfu/README.org index 48d591c91..e10d1b17d 100644 --- a/modules/completion/corfu/README.org +++ b/modules/completion/corfu/README.org @@ -17,6 +17,7 @@ utilities for fine-tuning. Only some of common behaviors are documented here. ** Maintainers - [[doom-user:][@LuigiPiucco]] +- [[doom-user:][@LemonBreezes]] [[doom-contrib-maintainer:][Become a maintainer?]] From e8897421b183cd5bc8a5b7c3dad52586d3120f22 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sun, 17 Mar 2024 08:01:37 -0400 Subject: [PATCH 09/25] fix(java): add lsp-treemacs lsp-java depends on lsp-treemacs without declaring it a dependency, so lsp-users using :lang (java +lsp) users without :ui (treemacs +lsp) will experience file-missing errors when lsp-java is loaded. --- modules/lang/java/packages.el | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/lang/java/packages.el b/modules/lang/java/packages.el index 8e84b5088..c51e11afe 100644 --- a/modules/lang/java/packages.el +++ b/modules/lang/java/packages.el @@ -14,4 +14,10 @@ (when (modulep! +lsp) (unless (modulep! :tools lsp +eglot) + ;; HACK: lsp-java depends on lsp-treemacs without declaring it as a + ;; dependency, thereby throwing errors if :ui (treemacs +lsp) isn't + ;; enabled (i.e. lsp-treemacs isn't installed). This needs to be tackled + ;; upstream, but for now: + (unless (alist-get 'lsp-treemacs doom-packages) + (package! lsp-treemacs :pin "e54e74deb8150964e3c3024e1ec14295a34e2a3b")) (package! lsp-java :pin "c962a3b3ac2beabdf1ce83b815396d6c38e3cefa"))) From b6e7bbbe07494df7c78f9e733454a87fb9672bbb Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Mon, 18 Mar 2024 20:45:11 -0400 Subject: [PATCH 10/25] fix(eval): overlay position Fix: #7732 Close: #7734 Co-authored-by: pysnow530 --- modules/tools/eval/autoload/eval.el | 49 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/modules/tools/eval/autoload/eval.el b/modules/tools/eval/autoload/eval.el index dbec32bcf..78ded4c82 100644 --- a/modules/tools/eval/autoload/eval.el +++ b/modules/tools/eval/autoload/eval.el @@ -24,34 +24,33 @@ (defun +eval-display-results-in-overlay (output &optional source-buffer) "Display OUTPUT in a floating overlay next to the cursor." (require 'eros) - (let* ((this-command #'+eval/buffer-or-region) - (prefix eros-eval-result-prefix) - (lines (split-string output "\n")) - (prefixlen (length prefix)) - (len (+ (apply #'max (mapcar #'length lines)) - prefixlen)) - (col (- (current-column) (window-hscroll))) - (next-line? (or (cdr lines) - (< (- (window-width) - (save-excursion (goto-char (point-at-eol)) - (- (current-column) - (window-hscroll)))) - len))) - (pad (if next-line? - (+ (window-hscroll) prefixlen) - 0)) - (where (if next-line? - (line-beginning-position 2) - (line-end-position))) - eros-eval-result-prefix - eros-overlays-use-font-lock) - (with-current-buffer (or source-buffer (current-buffer)) + (with-current-buffer (or source-buffer (current-buffer)) + (let* ((this-command #'+eval/buffer-or-region) + (prefix eros-eval-result-prefix) + (lines (split-string output "\n")) + (prefixlen (length prefix)) + (len (+ (apply #'max (mapcar #'length lines)) + prefixlen)) + (col (- (current-column) (window-hscroll))) + (next-line? (or (cdr lines) + (< (- (window-width) + (save-excursion (goto-char (line-end-position)) + (- (current-column) + (window-hscroll)))) + len))) + (pad (if next-line? + (+ (window-hscroll) prefixlen) + 0)) + eros-overlays-use-font-lock) (eros--make-result-overlay (concat (make-string (max 0 (- pad prefixlen)) ?\s) prefix - (string-join lines (concat "\n" (make-string pad ?\s)))) - :where where - :duration eros-eval-result-duration)))) + (string-join lines (concat hard-newline (make-string pad ?\s)))) + :where (if next-line? + (line-beginning-position 2) + (line-end-position)) + :duration eros-eval-result-duration + :format "%s")))) ;;;###autoload (defun +eval-display-results (output &optional source-buffer) From 5e7c769315941f8af1507bf659c5607e4baae960 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 20:30:11 -0400 Subject: [PATCH 11/25] fix: ensure inhibit-* is reset on startup error --- lisp/doom.el | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lisp/doom.el b/lisp/doom.el index 885e31f29..74ca41754 100644 --- a/lisp/doom.el +++ b/lisp/doom.el @@ -489,11 +489,18 @@ users).") inhibit-message nil) (redraw-frame)) (add-hook 'after-init-hook #'doom--reset-inhibited-vars-h) - (define-advice startup--load-user-init-file (:after (&rest _) undo-inhibit-vars) - (when init-file-had-error - (doom--reset-inhibited-vars-h)) - (unless (default-toplevel-value 'mode-line-format) - (setq-default mode-line-format (get 'mode-line-format 'initial-value)))) + (define-advice startup--load-user-init-file (:around (fn &rest args) undo-inhibit-vars) + (let (--init--) + (unwind-protect + (progn + (apply fn args) + (setq --init-- t)) + (when (or (not --init--) init-file-had-error) + ;; If we don't undo our inhibit-{message,redisplay} and there's an + ;; error, we'll see nothing but a blank Emacs frame. + (doom--reset-inhibited-vars-h)) + (unless (default-toplevel-value 'mode-line-format) + (setq-default mode-line-format (get 'mode-line-format 'initial-value)))))) ;; PERF: Doom disables the UI elements by default, so that there's less ;; for the frame to initialize. However, the toolbar is still populated From 1d9b102181455710860bb7ddc07d86f1eb7f260c Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 20:31:00 -0400 Subject: [PATCH 12/25] fix: shut up site-lisp One various OSes, Emacs ships with site-lisp files that load OS/architecture-specific config (like native-comp config), or load-lines for Emacs packages installed via your OS package manager (like mu4e). Output from these are rarely suppressed, for some reason, which causes noise in *Messages* at startup, which triggers a redraw, which can be very expensive during startup, depending on your window system. --- lisp/doom.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lisp/doom.el b/lisp/doom.el index 74ca41754..f695a1a67 100644 --- a/lisp/doom.el +++ b/lisp/doom.el @@ -489,10 +489,24 @@ users).") inhibit-message nil) (redraw-frame)) (add-hook 'after-init-hook #'doom--reset-inhibited-vars-h) + + ;; PERF,UX: An annoying aspect of site-lisp files is that they're often + ;; noisy (they emit load messages or other output to stdout). These + ;; queue unnecessary redraws at startup, cost startup time, and pollute + ;; the logs. I get around it by suppressing it until we can load it + ;; manually, later (in the `startup--load-user-init-file' advice below). + (put 'site-run-file 'initial-value site-run-file) + (setq site-run-file nil) + (define-advice startup--load-user-init-file (:around (fn &rest args) undo-inhibit-vars) (let (--init--) (unwind-protect (progn + (when (setq site-run-file (get 'site-run-file 'initial-value)) + (let ((inhibit-startup-screen inhibit-startup-screen)) + (letf! (defun load (file &optional noerror _nomessage &rest args) + (apply load file noerror t args)) + (load site-run-file t t)))) (apply fn args) (setq --init-- t)) (when (or (not --init--) init-file-had-error) From 1d99d1f191ecfd5c03489c380b6e39497e7ab8d6 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 20:32:00 -0400 Subject: [PATCH 13/25] refactor: consolidate startup--load-user-init-file advice Splitting up all this advice was unnecessary noise. --- lisp/doom.el | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lisp/doom.el b/lisp/doom.el index f695a1a67..5f819eb5f 100644 --- a/lisp/doom.el +++ b/lisp/doom.el @@ -437,13 +437,10 @@ users).") ;; PERF,UX: Site files tend to use `load-file', which emits "Loading X..." ;; messages in the echo area. Writing to the echo-area triggers a ;; redisplay, which can be expensive during startup. This may also cause - ;; an flash of white when creating the first frame. + ;; an flash of white when creating the first frame. Needs to be undo + ;; later, though. (define-advice load-file (:override (file) silence) (load file nil 'nomessage)) - ;; COMPAT: But undo our `load-file' advice later, as to limit the scope of - ;; any edge cases it could induce. - (define-advice startup--load-user-init-file (:before (&rest _) undo-silence) - (advice-remove #'load-file #'load-file@silence)) ;; PERF: `load-suffixes' and `load-file-rep-suffixes' are consulted on ;; each `require' and `load'. Doom won't load any modules this early, so @@ -462,13 +459,18 @@ users).") ;; PERF: Doom uses `defcustom' to indicate variables that users are ;; expected to reconfigure. Trouble is it fires off initializers meant ;; to accommodate any user attempts to configure them before they were - ;; defined. This is unnecessary before $DOOMDIR/init.el is loaded, so I - ;; disable them until it is. + ;; defined. This is unnecessary work before $DOOMDIR/init.el is loaded, + ;; so I disable them until it is. (setq custom-dont-initialize t) (add-hook! 'doom-before-init-hook (defun doom--reset-custom-dont-initialize-h () (setq custom-dont-initialize nil))) + ;; PERF: Doom disables the UI elements by default, so that there's less + ;; for the frame to initialize. However, the toolbar is still populated + ;; regardless, so I lazy load it until tool-bar-mode is actually used. + (advice-add #'tool-bar-setup :override #'ignore) + ;; PERF: The mode-line procs a couple dozen times during startup. This is ;; normally quite fast, but disabling the default mode-line and reducing ;; the update delay timer seems to stave off ~30-50ms. @@ -476,8 +478,8 @@ users).") (setq-default mode-line-format nil) (dolist (buf (buffer-list)) (with-current-buffer buf (setq mode-line-format nil))) - ;; PERF,UX: Premature redisplays can substantially affect startup times and - ;; produce ugly flashes of unstyled Emacs. + ;; PERF,UX: Premature redisplays can substantially affect startup times + ;; and/or produce ugly flashes of unstyled Emacs. (setq-default inhibit-redisplay t inhibit-message t) ;; COMPAT: Then reset with advice, because `startup--load-user-init-file' @@ -502,11 +504,17 @@ users).") (let (--init--) (unwind-protect (progn + ;; COMPAT: Onces startup is sufficiently complete, undo some + ;; optimizations to reduce the scope of potential edge cases. + (advice-remove #'load-file #'load-file@silence) + (advice-remove #'tool-bar-setup #'ignore) + (add-transient-hook! 'tool-bar-mode (tool-bar-setup)) (when (setq site-run-file (get 'site-run-file 'initial-value)) (let ((inhibit-startup-screen inhibit-startup-screen)) (letf! (defun load (file &optional noerror _nomessage &rest args) (apply load file noerror t args)) (load site-run-file t t)))) + ;; Then startup as normal. (apply fn args) (setq --init-- t)) (when (or (not --init--) init-file-had-error) @@ -516,14 +524,6 @@ users).") (unless (default-toplevel-value 'mode-line-format) (setq-default mode-line-format (get 'mode-line-format 'initial-value)))))) - ;; PERF: Doom disables the UI elements by default, so that there's less - ;; for the frame to initialize. However, the toolbar is still populated - ;; regardless, so I lazy load it until tool-bar-mode is actually used. - (advice-add #'tool-bar-setup :override #'ignore) - (define-advice startup--load-user-init-file (:before (&rest _) defer-tool-bar-setup) - (advice-remove #'tool-bar-setup #'ignore) - (add-transient-hook! 'tool-bar-mode (tool-bar-setup))) - ;; PERF: Unset a non-trivial list of command line options that aren't ;; relevant to this session, but `command-line-1' still processes. (unless doom--system-macos-p From 6b55c6adc6056d2337240ffcb403324fb739be05 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 20:33:39 -0400 Subject: [PATCH 14/25] refactor: inline doom--make-font-specs This function isn't (and won't be) used anywhere else. No reason for it to be its own function. --- lisp/doom-ui.el | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/lisp/doom-ui.el b/lisp/doom-ui.el index 95d5b92ce..64db32afd 100644 --- a/lisp/doom-ui.el +++ b/lisp/doom-ui.el @@ -498,23 +498,6 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (cons 'custom-theme-directory (delq 'custom-theme-directory custom-theme-load-path))) -(defun doom--make-font-specs (face font frame) - (let* ((base-specs (cadr (assq 'user (get face 'theme-face)))) - (base-specs (or base-specs '((t nil)))) - (attrs '(:family :foundry :slant :weight :height :width)) - (new-specs nil)) - (dolist (spec base-specs) - ;; Each SPEC has the form (DISPLAY ATTRIBUTE-PLIST) - (let ((display (car spec)) - (plist (copy-tree (nth 1 spec)))) - ;; Alter only DISPLAY conditions matching this frame. - (when (or (memq display '(t default)) - (face-spec-set-match-display display frame)) - (dolist (attr attrs) - (setq plist (plist-put plist attr (face-attribute face attr))))) - (push (list display plist) new-specs))) - (nreverse new-specs))) - (defun doom-init-fonts-h (&optional _reload) "Loads `doom-font', `doom-serif-font', and `doom-variable-pitch-font'." (let ((this-frame (selected-frame))) @@ -530,11 +513,24 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (set-face-attribute face frame :width 'normal :weight 'normal :slant 'normal :font font))) - (let ((new-specs (doom--make-font-specs face font this-frame))) - ;; Don't save to `customized-face' so it's omitted from `custom-file' - ;;(put face 'customized-face new-specs) - (custom-push-theme 'theme-face face 'user 'set new-specs) - (put face 'face-modified nil))) + (custom-push-theme + 'theme-face face 'user 'set + (let* ((base-specs (cadr (assq 'user (get face 'theme-face)))) + (base-specs (or base-specs '((t nil)))) + (attrs '(:family :foundry :slant :weight :height :width)) + (new-specs nil)) + (dolist (spec base-specs) + ;; Each SPEC has the form (DISPLAY ATTRIBUTE-PLIST) + (let ((display (car spec)) + (plist (copy-tree (nth 1 spec)))) + ;; Alter only DISPLAY conditions matching this frame. + (when (or (memq display '(t default)) + (face-spec-set-match-display display this-frame)) + (dolist (attr attrs) + (setq plist (plist-put plist attr (face-attribute face attr))))) + (push (list display plist) new-specs))) + (nreverse new-specs))) + (put face 'face-modified nil)) ('error (ignore-errors (doom--reset-inhibited-vars-h)) (if (string-prefix-p "Font not available" (error-message-string e)) From d553ebc930d7d3d8a65740059250cfd4c037cc37 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 20:34:10 -0400 Subject: [PATCH 15/25] fix: invoke debugger for early-init.el-level errors --- early-init.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/early-init.el b/early-init.el index bc6032b42..9d6778109 100644 --- a/early-init.el +++ b/early-init.el @@ -109,7 +109,7 @@ ;; I avoid `load's NOERROR argument because it suppresses other, ;; legitimate errors (like permission or IO errors), which gets ;; incorrectly interpreted as "this is not a Doom config". - (condition-case _ + (condition-case-unless-debug _ ;; Load the heart of Doom Emacs. (load (expand-file-name "lisp/doom" user-emacs-directory) nil (not init-file-debug) nil 'must-suffix) From 550d6ecd1984cc425ed89aa422ea0ce9119f8d27 Mon Sep 17 00:00:00 2001 From: Sean Farley Date: Tue, 19 Mar 2024 11:39:47 -0700 Subject: [PATCH 16/25] bump: :tools magit magit/forge@03b48be2a12a -> magit/forge@03b48be2a12a magit/magit@65ecb9c5fc75 -> magit/magit@65ecb9c5fc75 This fixes a bug in forge (which has been fixed upstream) that erroneously set `minibuffer-allow-text-properties' globally and caused seemingly random errors involving completion from the minibuffer. Ref: magit/forge#639 Ref: minad/jinx#140 --- modules/tools/magit/packages.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/tools/magit/packages.el b/modules/tools/magit/packages.el index 6bc86d4e5..7bebc3a57 100644 --- a/modules/tools/magit/packages.el +++ b/modules/tools/magit/packages.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/magit/packages.el -(when (package! magit :pin "65ecb9c5fc7586a1c527b60d180a97ea230da99f") +(when (package! magit :pin "0963697f24cfbe80f92312044bd9ab28b914b053") (when (modulep! +forge) - (package! forge :pin "03b48be2a12a282cd47b92287fc1701a81f1cece") + (package! forge :pin "68771ca4d53c3aea5c860eeb888cee8e9cb5ca37") (package! code-review :recipe (:host github :repo "doomelpa/code-review" From 9447e820740edbc3f3ce9a296be934ca635e0131 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 19:49:54 -0400 Subject: [PATCH 17/25] bump: :completion vertico minad/consult-flycheck@d83f87581af7 -> minad/consult-flycheck@754f5497d827 minad/consult@9463146ba754 -> minad/consult@b48ff6bf0527 minad/marginalia@ea356ebb1ddb -> minad/marginalia@f6fe86b989a1 minad/vertico@4a7da56b02c6 -> minad/vertico@68cbd4758944 oantolin/embark@60139db8794f -> oantolin/embark@c93abadc8220 oantolin/orderless@b24748093b00 -> oantolin/orderless@dc7a781acf2e --- modules/completion/vertico/packages.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/completion/vertico/packages.el b/modules/completion/vertico/packages.el index 348f43ce7..787d22140 100644 --- a/modules/completion/vertico/packages.el +++ b/modules/completion/vertico/packages.el @@ -1,19 +1,19 @@ ;; -*- no-byte-compile: t; -*- ;;; completion/vertico/packages.el -(package! vertico :pin "4a7da56b02c6aefff8f6b4574a530a7cb54bc21a") +(package! vertico :pin "68cbd47589446e9674921bae0b98ff8fbe28be23") -(package! orderless :pin "b24748093b00b37c3a572c4909f61c08fa27504f") +(package! orderless :pin "dc7a781acf2e58ac7d20d1b522be0cde5213e057") -(package! consult :pin "9463146ba754103db9475ae56e46561366ba4773") +(package! consult :pin "b48ff6bf0527baeb6bfd07c6da9d303ff0b79c3d") (package! consult-dir :pin "3f5f4b71ebe819392cb090cda71bd39a93bd830a") (when (and (modulep! :checkers syntax) (not (modulep! :checkers syntax +flymake))) - (package! consult-flycheck :pin "d83f87581af74f7a2739d8b1b90c37da5ae3d310")) -(package! embark :pin "60139db8794f7e4a08076d9f7597d08f6c8083d1") -(package! embark-consult :pin "60139db8794f7e4a08076d9f7597d08f6c8083d1") + (package! consult-flycheck :pin "754f5497d827f7d58009256a21af614cc44378a3")) +(package! embark :pin "c93abadc8220c0caa6fea805f7a736c346d47e7e") +(package! embark-consult :pin "c93abadc8220c0caa6fea805f7a736c346d47e7e") -(package! marginalia :pin "ea356ebb1ddb8d6da78574b517155475cf52d46f") +(package! marginalia :pin "f6fe86b989a177355ab3ff7e97a384e10a7b0bb1") (package! wgrep :pin "208b9d01cfffa71037527e3a324684b3ce45ddc4") From f71689304e057eeefb0b9000c92518593c842e12 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 21:30:16 -0400 Subject: [PATCH 18/25] tweak: enable startup optimizations in debug mode Before this, startup optimizations were disabled in debug mode, but more often than not, this just made it difficult to reproduce some errors at startup. --- lisp/doom.el | 179 +++++++++++++++++++++++++-------------------------- 1 file changed, 89 insertions(+), 90 deletions(-) diff --git a/lisp/doom.el b/lisp/doom.el index 5f819eb5f..9545c215a 100644 --- a/lisp/doom.el +++ b/lisp/doom.el @@ -433,103 +433,102 @@ users).") (doom-partial #'tty-run-terminal-initialization (selected-frame) nil t)))) - (unless init-file-debug - ;; PERF,UX: Site files tend to use `load-file', which emits "Loading X..." - ;; messages in the echo area. Writing to the echo-area triggers a - ;; redisplay, which can be expensive during startup. This may also cause - ;; an flash of white when creating the first frame. Needs to be undo - ;; later, though. - (define-advice load-file (:override (file) silence) - (load file nil 'nomessage)) + ;; PERF,UX: Site files tend to use `load-file', which emits "Loading X..." + ;; messages in the echo area. Writing to the echo-area triggers a + ;; redisplay, which can be expensive during startup. This may also cause + ;; an flash of white when creating the first frame. Needs to be undo + ;; later, though. + (define-advice load-file (:override (file) silence) + (load file nil 'nomessage)) - ;; PERF: `load-suffixes' and `load-file-rep-suffixes' are consulted on - ;; each `require' and `load'. Doom won't load any modules this early, so - ;; omit .so for a tiny startup boost. Is later restored in doom-start. - (put 'load-suffixes 'initial-value (default-toplevel-value 'load-suffixes)) - (put 'load-file-rep-suffixes 'initial-value (default-toplevel-value 'load-file-rep-suffixes)) - (set-default-toplevel-value 'load-suffixes '(".elc" ".el")) - (set-default-toplevel-value 'load-file-rep-suffixes '("")) - ;; COMPAT: Undo any problematic startup optimizations; from this point, I - ;; make no assumptions about what might be loaded in userland. - (add-hook! 'doom-before-init-hook - (defun doom--reset-load-suffixes-h () - (setq load-suffixes (get 'load-suffixes 'initial-value) - load-file-rep-suffixes (get 'load-file-rep-suffixes 'initial-value)))) + ;; PERF: `load-suffixes' and `load-file-rep-suffixes' are consulted on + ;; each `require' and `load'. Doom won't load any modules this early, so + ;; omit .so for a tiny startup boost. Is later restored in doom-start. + (put 'load-suffixes 'initial-value (default-toplevel-value 'load-suffixes)) + (put 'load-file-rep-suffixes 'initial-value (default-toplevel-value 'load-file-rep-suffixes)) + (set-default-toplevel-value 'load-suffixes '(".elc" ".el")) + (set-default-toplevel-value 'load-file-rep-suffixes '("")) + ;; COMPAT: Undo any problematic startup optimizations; from this point, I + ;; make no assumptions about what might be loaded in userland. + (add-hook! 'doom-before-init-hook + (defun doom--reset-load-suffixes-h () + (setq load-suffixes (get 'load-suffixes 'initial-value) + load-file-rep-suffixes (get 'load-file-rep-suffixes 'initial-value)))) - ;; PERF: Doom uses `defcustom' to indicate variables that users are - ;; expected to reconfigure. Trouble is it fires off initializers meant - ;; to accommodate any user attempts to configure them before they were - ;; defined. This is unnecessary work before $DOOMDIR/init.el is loaded, - ;; so I disable them until it is. - (setq custom-dont-initialize t) - (add-hook! 'doom-before-init-hook - (defun doom--reset-custom-dont-initialize-h () - (setq custom-dont-initialize nil))) + ;; PERF: Doom uses `defcustom' to indicate variables that users are + ;; expected to reconfigure. Trouble is it fires off initializers meant + ;; to accommodate any user attempts to configure them before they were + ;; defined. This is unnecessary work before $DOOMDIR/init.el is loaded, + ;; so I disable them until it is. + (setq custom-dont-initialize t) + (add-hook! 'doom-before-init-hook + (defun doom--reset-custom-dont-initialize-h () + (setq custom-dont-initialize nil))) - ;; PERF: Doom disables the UI elements by default, so that there's less - ;; for the frame to initialize. However, the toolbar is still populated - ;; regardless, so I lazy load it until tool-bar-mode is actually used. - (advice-add #'tool-bar-setup :override #'ignore) + ;; PERF: Doom disables the UI elements by default, so that there's less + ;; for the frame to initialize. However, the toolbar is still populated + ;; regardless, so I lazy load it until tool-bar-mode is actually used. + (advice-add #'tool-bar-setup :override #'ignore) - ;; PERF: The mode-line procs a couple dozen times during startup. This is - ;; normally quite fast, but disabling the default mode-line and reducing - ;; the update delay timer seems to stave off ~30-50ms. - (put 'mode-line-format 'initial-value (default-toplevel-value 'mode-line-format)) - (setq-default mode-line-format nil) - (dolist (buf (buffer-list)) - (with-current-buffer buf (setq mode-line-format nil))) - ;; PERF,UX: Premature redisplays can substantially affect startup times - ;; and/or produce ugly flashes of unstyled Emacs. - (setq-default inhibit-redisplay t - inhibit-message t) - ;; COMPAT: Then reset with advice, because `startup--load-user-init-file' - ;; will never be interrupted by errors. And if these settings are left - ;; set, Emacs could appear frozen or garbled. - (defun doom--reset-inhibited-vars-h () - (setq-default inhibit-redisplay nil - ;; Inhibiting `message' only prevents redraws and - inhibit-message nil) - (redraw-frame)) - (add-hook 'after-init-hook #'doom--reset-inhibited-vars-h) + ;; PERF: The mode-line procs a couple dozen times during startup. This is + ;; normally quite fast, but disabling the default mode-line and reducing + ;; the update delay timer seems to stave off ~30-50ms. + (put 'mode-line-format 'initial-value (default-toplevel-value 'mode-line-format)) + (setq-default mode-line-format nil) + (dolist (buf (buffer-list)) + (with-current-buffer buf (setq mode-line-format nil))) + ;; PERF,UX: Premature redisplays can substantially affect startup times + ;; and/or produce ugly flashes of unstyled Emacs. + (setq-default inhibit-redisplay t + inhibit-message t) + ;; COMPAT: Then reset with advice, because `startup--load-user-init-file' + ;; will never be interrupted by errors. And if these settings are left + ;; set, Emacs could appear frozen or garbled. + (defun doom--reset-inhibited-vars-h () + (setq-default inhibit-redisplay nil + ;; Inhibiting `message' only prevents redraws and + inhibit-message nil) + (redraw-frame)) + (add-hook 'after-init-hook #'doom--reset-inhibited-vars-h) - ;; PERF,UX: An annoying aspect of site-lisp files is that they're often - ;; noisy (they emit load messages or other output to stdout). These - ;; queue unnecessary redraws at startup, cost startup time, and pollute - ;; the logs. I get around it by suppressing it until we can load it - ;; manually, later (in the `startup--load-user-init-file' advice below). - (put 'site-run-file 'initial-value site-run-file) - (setq site-run-file nil) + ;; PERF,UX: An annoying aspect of site-lisp files is that they're often + ;; noisy (they emit load messages or other output to stdout). These + ;; queue unnecessary redraws at startup, cost startup time, and pollute + ;; the logs. I get around it by suppressing it until we can load it + ;; manually, later (in the `startup--load-user-init-file' advice below). + (put 'site-run-file 'initial-value site-run-file) + (setq site-run-file nil) - (define-advice startup--load-user-init-file (:around (fn &rest args) undo-inhibit-vars) - (let (--init--) - (unwind-protect - (progn - ;; COMPAT: Onces startup is sufficiently complete, undo some - ;; optimizations to reduce the scope of potential edge cases. - (advice-remove #'load-file #'load-file@silence) - (advice-remove #'tool-bar-setup #'ignore) - (add-transient-hook! 'tool-bar-mode (tool-bar-setup)) - (when (setq site-run-file (get 'site-run-file 'initial-value)) - (let ((inhibit-startup-screen inhibit-startup-screen)) - (letf! (defun load (file &optional noerror _nomessage &rest args) - (apply load file noerror t args)) - (load site-run-file t t)))) - ;; Then startup as normal. - (apply fn args) - (setq --init-- t)) - (when (or (not --init--) init-file-had-error) - ;; If we don't undo our inhibit-{message,redisplay} and there's an - ;; error, we'll see nothing but a blank Emacs frame. - (doom--reset-inhibited-vars-h)) - (unless (default-toplevel-value 'mode-line-format) - (setq-default mode-line-format (get 'mode-line-format 'initial-value)))))) + (define-advice startup--load-user-init-file (:around (fn &rest args) undo-inhibit-vars) + (let (--init--) + (unwind-protect + (progn + ;; COMPAT: Onces startup is sufficiently complete, undo some + ;; optimizations to reduce the scope of potential edge cases. + (advice-remove #'load-file #'load-file@silence) + (advice-remove #'tool-bar-setup #'ignore) + (add-transient-hook! 'tool-bar-mode (tool-bar-setup)) + (when (setq site-run-file (get 'site-run-file 'initial-value)) + (let ((inhibit-startup-screen inhibit-startup-screen)) + (letf! (defun load (file &optional noerror _nomessage &rest args) + (apply load file noerror t args)) + (load site-run-file t t)))) + ;; Then startup as normal. + (apply fn args) + (setq --init-- t)) + (when (or (not --init--) init-file-had-error) + ;; If we don't undo our inhibit-{message,redisplay} and there's an + ;; error, we'll see nothing but a blank Emacs frame. + (doom--reset-inhibited-vars-h)) + (unless (default-toplevel-value 'mode-line-format) + (setq-default mode-line-format (get 'mode-line-format 'initial-value)))))) - ;; PERF: Unset a non-trivial list of command line options that aren't - ;; relevant to this session, but `command-line-1' still processes. - (unless doom--system-macos-p - (setq command-line-ns-option-alist nil)) - (unless (memq initial-window-system '(x pgtk)) - (setq command-line-x-option-alist nil))))) + ;; PERF: Unset a non-trivial list of command line options that aren't + ;; relevant to this session, but `command-line-1' still processes. + (unless doom--system-macos-p + (setq command-line-ns-option-alist nil)) + (unless (memq initial-window-system '(x pgtk)) + (setq command-line-x-option-alist nil)))) ;; From 875cd1aef99ca19d4534b4fbbb6bc931e2e10440 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 21:34:03 -0400 Subject: [PATCH 19/25] fix(corfu): gate corfu-terminal config If :completion corfu users don't have :os tty enabled, loading corfu-terminal will throw an error in TTY Emacs. --- modules/completion/corfu/config.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index defdc8aaa..06dcd83cd 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -133,9 +133,11 @@ use the minibuffer such as `query-replace'.") (add-hook 'completion-at-point-functions #'yasnippet-capf 30 t)))) (use-package! corfu-terminal + :when (modulep! :os tty) :when (not (display-graphic-p)) :hook ((corfu-mode . corfu-terminal-mode))) + ;; ;;; Extensions From b52d2b2dd06a563e6f56aa2e36cb6e8c011b7061 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 21:41:50 -0400 Subject: [PATCH 20/25] fix(corfu): ispell: only complain once per session --- modules/completion/corfu/config.el | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index 06dcd83cd..d6948c90a 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -83,7 +83,21 @@ use the minibuffer such as `query-replace'.") (when (member (bound-and-true-p evil--ex-search-update-timer) timer-idle-list) (apply (timer--function evil--ex-search-update-timer) - (timer--args evil--ex-search-update-timer)))))) + (timer--args evil--ex-search-update-timer))))) + + ;; HACK: If your dictionaries aren't set up in text-mode buffers, ispell will + ;; continuously pester you about errors. This ensures it only happens once + ;; per session. + (defadvice! +corfu--auto-disable-ispell-capf-a (fn &rest args) + "If ispell isn't properly set up, only complain once per session." + :around #'ispell-completion-at-point + (condition-case-unless-debug e + (apply fn args) + ('error + (message "Error: %s" (error-message-string e)) + (message "Auto-disabling `text-mode-ispell-word-completion'") + (setq text-mode-ispell-word-completion nil) + (remove-hook 'completion-at-point-functions #'ispell-completion-at-point t))))) (use-package! cape :defer t From c9c221ca5992aa98495a934fa6a85c98fdbefeb8 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 21:46:31 -0400 Subject: [PATCH 21/25] fix(corfu): wrong-type-argument characterp error Corfu doesn't support a nil corfu-separator in general, but +corfu-smart-sep-toggle-escape, specifically, will throw a characterp type error if the user hasn't enabled +orderless. --- modules/completion/corfu/config.el | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index d6948c90a..abd90c559 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -51,7 +51,6 @@ use the minibuffer such as `query-replace'.") vterm-mode) t) corfu-cycle t - corfu-separator (when (modulep! +orderless) ?\s) corfu-preselect 'prompt corfu-count 16 corfu-max-width 120 From 7547cdac6d7da88bdee2abb9f9ddb2f7d10f7122 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 19 Mar 2024 21:49:23 -0400 Subject: [PATCH 22/25] refactor(corfu): remove redundant setting Upstream, corfu-preview-current is already 'insert by default. --- modules/completion/corfu/config.el | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/completion/corfu/config.el b/modules/completion/corfu/config.el index abd90c559..475894fa5 100644 --- a/modules/completion/corfu/config.el +++ b/modules/completion/corfu/config.el @@ -54,7 +54,6 @@ use the minibuffer such as `query-replace'.") corfu-preselect 'prompt corfu-count 16 corfu-max-width 120 - corfu-preview-current 'insert corfu-on-exact-match nil corfu-quit-at-boundary (if (modulep! +orderless) 'separator t) corfu-quit-no-match (if (modulep! +orderless) 'separator t) From c564c17a6ba83d2864fe532191eca4398794f996 Mon Sep 17 00:00:00 2001 From: Ian McCowan Date: Mon, 6 Nov 2023 09:11:09 -0800 Subject: [PATCH 23/25] fix(fold): reorder fold type checks (+fold--ts-fold-p) just checks that the mode is active so it will block any checks following it. Move it to the end so other fold types have a chance, and consistently order fold type checks. --- modules/editor/fold/autoload/fold.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/editor/fold/autoload/fold.el b/modules/editor/fold/autoload/fold.el index 2b757cc87..1052374bb 100644 --- a/modules/editor/fold/autoload/fold.el +++ b/modules/editor/fold/autoload/fold.el @@ -75,8 +75,8 @@ Targets `vimmish-fold', `hideshow', `ts-fold' and `outline' folds." (cl-letf (((symbol-function #'outline-hide-subtree) (symbol-function #'outline-hide-entry))) (outline-toggle-children))) - ((+fold--ts-fold-p) (ts-fold-toggle)) - ((+fold--hideshow-fold-p) (+fold-from-eol (hs-toggle-hiding)))))) + ((+fold--hideshow-fold-p) (+fold-from-eol (hs-toggle-hiding))) + ((+fold--ts-fold-p) (ts-fold-toggle))))) ;;;###autoload (defun +fold/open () @@ -89,8 +89,8 @@ Targets `vimmish-fold', `hideshow', `ts-fold' and `outline' folds." ((+fold--outline-fold-p) (outline-show-children) (outline-show-entry)) - ((+fold--ts-fold-p) (ts-fold-open)) - ((+fold--hideshow-fold-p) (+fold-from-eol (hs-show-block)))))) + ((+fold--hideshow-fold-p) (+fold-from-eol (hs-show-block))) + ((+fold--ts-fold-p) (ts-fold-open))))) ;;;###autoload (defun +fold/close () @@ -100,9 +100,9 @@ Targets `vimmish-fold', `hideshow', `ts-fold' and `outline' folds." (interactive) (save-excursion (cond ((+fold--vimish-fold-p) (vimish-fold-refold)) - ((+fold--ts-fold-p) (ts-fold-close)) + ((+fold--outline-fold-p) (outline-hide-subtree)) ((+fold--hideshow-fold-p) (+fold-from-eol (hs-hide-block))) - ((+fold--outline-fold-p) (outline-hide-subtree))))) + ((+fold--ts-fold-p) (ts-fold-close))))) ;;;###autoload (defun +fold/open-all (&optional level) From 0ea84d1c3bff76cdf8983bd033717631016fb31b Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Wed, 20 Mar 2024 00:03:37 -0400 Subject: [PATCH 24/25] feat(vertico): add consult-yasnippet Close: #7471 Co-authored-by: LemonBreezes --- modules/completion/vertico/config.el | 5 +++++ modules/completion/vertico/packages.el | 3 +++ 2 files changed, 8 insertions(+) diff --git a/modules/completion/vertico/config.el b/modules/completion/vertico/config.el index 3f2bf3f30..e15bd0fb7 100644 --- a/modules/completion/vertico/config.el +++ b/modules/completion/vertico/config.el @@ -251,6 +251,11 @@ orderless." (not (modulep! :checkers syntax +flymake))) :after (consult flycheck)) +(use-package! consult-yasnippet + :when (modulep! :editor snippets) + :defer t + :init (map! [remap yas-insert-snippet] #'consult-yasnippet)) + (use-package! embark :defer t diff --git a/modules/completion/vertico/packages.el b/modules/completion/vertico/packages.el index 787d22140..8d1f9c75f 100644 --- a/modules/completion/vertico/packages.el +++ b/modules/completion/vertico/packages.el @@ -24,3 +24,6 @@ (package! vertico-posframe :recipe (:host github :repo "tumashu/vertico-posframe") :pin "2e0e09e5bbd6ec576ddbe566ab122575ef051fab")) + +(when (modulep! :editor snippets) + (package! consult-yasnippet :pin "834d39acfe8a7d2c304afbe4d649b9372118c756")) From 73f19acb66ff37d2ef1644516c0c983543ac2246 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Wed, 20 Mar 2024 00:12:23 -0400 Subject: [PATCH 25/25] fix(julia): revise julia-snail settings - julia-snail-multimedia-enable is buffer-local, so setq-default is needed. - julia-snail-popup-display-eval-results is already :command upstream. - julia-snail will automatically calculate a (reasonable) value for julia-snail-popup-display-face in the absence of an explicit setting. - julia-snail-popup-display-face was filled with references to doom-themes symbols that aren't global. It's any wonder they worked before this. Besidse, it's best we not couple this module with one specific theme (or theme pack in this case). Close: #7625 Co-authored-by: ngharrison --- modules/lang/julia/config.el | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/lang/julia/config.el b/modules/lang/julia/config.el index 6f3f33358..657574431 100644 --- a/modules/lang/julia/config.el +++ b/modules/lang/julia/config.el @@ -106,12 +106,10 @@ :when (modulep! :term vterm) :hook (julia-mode . julia-snail-mode) :config - (setq julia-snail-popup-display-eval-results :command) - (setq julia-snail-multimedia-enable t) - (setq julia-snail-popup-display-face '(:background base3 :box `(:line-width -1 :color base5))) - (set-popup-rule! "^\\*julia.*\\*$" :ttl nil :select nil :quit nil) + (setq-default julia-snail-multimedia-enable t) + (after! julia-mode (set-repl-handler! 'julia-mode #'+julia/open-snail-repl :persist t