about summary refs log tree commit diff stats
path: root/.emacs.d/lisp/init-minibuffer.el
diff options
context:
space:
mode:
Diffstat (limited to '.emacs.d/lisp/init-minibuffer.el')
-rw-r--r--.emacs.d/lisp/init-minibuffer.el276
1 files changed, 205 insertions, 71 deletions
diff --git a/.emacs.d/lisp/init-minibuffer.el b/.emacs.d/lisp/init-minibuffer.el
index 9fbcf0c..118f730 100644
--- a/.emacs.d/lisp/init-minibuffer.el
+++ b/.emacs.d/lisp/init-minibuffer.el
@@ -12,7 +12,7 @@
   :config
   ;; Do not allow the cursor in the minibuffer prompt
   (setq minibuffer-prompt-properties
-        '(read-only t cursor-intangible t face minibuffer-prompt))
+        '(read-only t cursor-intangible t intangible t face minibuffer-prompt))
   (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
   (setq enable-recursive-minibuffers t)
   (minibuffer-depth-indicate-mode t)
@@ -42,24 +42,31 @@
                    "  ")
                  cand)))
 
+  ;; https://github.com/minad/vertico/wiki#ding-when-wrapping-around
+  (advice-add #'vertico-next
+              :around
+              #'(lambda (origin &rest args)
+                  (let ((beg-index vertico--index))
+                    (apply origin args)
+                    (if (not (eq 1 (abs (- beg-index vertico--index))))
+                        (ding)))))
+
+  ;; https://github.com/minad/vertico/wiki#useful-commands-from-outside-minibuffer
   (defun down-from-outside ()
     "Move to next candidate in minibuffer, even when minibuffer isn't selected."
     (interactive)
     (with-selected-window (active-minibuffer-window)
       (execute-kbd-macro [down])))
-
   (defun up-from-outside ()
     "Move to previous candidate in minibuffer, even when minibuffer isn't selected."
     (interactive)
     (with-selected-window (active-minibuffer-window)
       (execute-kbd-macro [up])))
-
   (defun preview-from-outside ()
     "Preview the selected candidate, even when minibuffer isn't selected."
     (interactive)
     (with-selected-window (active-minibuffer-window)
       (execute-kbd-macro (kbd "M-."))))
-
   (defun to-and-fro-minibuffer ()
     "Go back and forth between minibuffer and other window."
     (interactive)
@@ -67,18 +74,73 @@
         (select-window (minibuffer-selected-window))
       (select-window (active-minibuffer-window))))
 
-  (defun minibuffer-really-quit ()
-    "Quit minibuffer session, even if it is not the selected window."
+  ;; Modified from https://github.com/minad/vertico/wiki#update-minibuffer-history-with-candidate-insertions
+  (defadvice vertico-insert
+      (after vertico-insert-add-history activate)
+    "Make vertico-insert add to the minibuffer history."
+    (if (and (not (eq minibuffer-history-variable t))
+             (eq 'file (vertico--metadata-get 'category)))
+        (add-to-history minibuffer-history-variable (minibuffer-contents))))
+
+  (defun toggle-sort-directories-first ()
     (interactive)
-    (with-selected-window (active-minibuffer-window)
-      (minibuffer-keyboard-quit)))
+    (if (eq vertico-sort-function 'vertico-sort-directories-first)
+        (set (make-local-variable 'vertico-sort-function) 'vertico-sort-history-length-alpha)
+      (set (make-local-variable 'vertico-sort-function) 'vertico-sort-directories-first))
+    (setq vertico--input t)
+    (vertico--update))
 
   :bind (("C-M-<" . up-from-outside)
          ("C-M->" . down-from-outside)
          ("C-M-+" . preview-from-outside)
          ("M-X" . to-and-fro-minibuffer)
-         ("C-M-S-g" . minibuffer-really-quit)
-         (:map vertico-map ("M-RET" . minibuffer-force-complete-and-exit))))
+         (:map vertico-map
+               ("M-RET" . minibuffer-force-complete-and-exit)
+               ("M-D" . toggle-sort-directories-first))))
+
+(use-extension vertico vertico-multiform
+  :config
+  (vertico-multiform-mode +1)
+
+  (defun vertico-multiform-buffer-grid ()
+    "Toggle displaying Vertico as a grid in a large window (like a regular buffer).)"
+    (interactive)
+    (if (equal '(vertico-buffer-mode vertico-grid-mode) (car vertico-multiform--stack))
+        (vertico-multiform-vertical)
+      (setcar vertico-multiform--stack '(vertico-buffer-mode vertico-grid-mode))
+      (vertico-multiform--toggle 1)))
+
+  ;; https://github.com/minad/vertico/wiki#candidate-display-transformations-custom-candidate-highlighting
+  (defvar +vertico-transform-functions nil)
+  (cl-defmethod vertico--format-candidate :around
+    (cand prefix suffix index start &context ((not +vertico-transform-functions) null))
+    (dolist (fun (ensure-list +vertico-transform-functions))
+      (setq cand (funcall fun cand)))
+    (cl-call-next-method cand prefix suffix index start))
+  (defun +vertico-highlight-directory (file)
+    "If FILE ends with a slash, highlight it as a directory."
+    (if (string-suffix-p "/" file)
+        (propertize file 'face 'marginalia-file-priv-dir)
+      file))
+  (defun +vertico-highlight-enabled-mode (cmd)
+    "If MODE is enabled, highlight it as font-lock-doc-face."
+    (let ((sym (intern cmd)))
+      (if (or (eq sym major-mode)
+              (and
+               (memq sym minor-mode-list)
+               (boundp sym)))
+          (propertize cmd 'face 'font-lock-doc-face)
+        cmd)))
+
+  (setq vertico-multiform-commands
+        '((execute-extended-command
+           (+vertico-transform-functions . +vertico-highlight-enabled-mode))))
+  (setq vertico-multiform-categories
+        '((file (+vertico-transform-functions . +vertico-highlight-directory)
+                (:keymap . vertico-directory-map))
+          (imenu grid)))
+  :bind (:map vertico-multiform-map
+              ("M-H" . vertico-multiform-buffer-grid)))
 
 (use-extension vertico vertico-directory
   :config
@@ -115,28 +177,28 @@
 DEFS is a plist associating completion categories to commands."
     (let ((default-command (lookup-key vertico-map (kbd key))))
       (define-key vertico-map (kbd key)
-        (list 'menu-item nil defs :filter
-              (lambda (d)
-                (or (plist-get d (completion-metadata-get
-                                  (completion-metadata (minibuffer-contents)
-                                                       minibuffer-completion-table
-                                                       minibuffer-completion-predicate)
-                                  'category))
-                    default-command))))))
+                  (list 'menu-item nil defs :filter
+                        (lambda (d)
+                          (or (plist-get d (completion-metadata-get
+                                            (completion-metadata (minibuffer-contents)
+                                                                 minibuffer-completion-table
+                                                                 minibuffer-completion-predicate)
+                                            'category))
+                              default-command))))))
   (define-vertico-key "/"
-    'file #'vertico-directory-slash
-    'project-file #'vertico-directory-slash)
+                      'file #'vertico-directory-slash
+                      'project-file #'vertico-directory-slash)
   (define-vertico-key "RET"
-    'file #'vertico-directory-enter-or-select-project
-    'project-file #'vertico-directory-enter)
+                      'file #'vertico-directory-enter-or-select-project
+                      'project-file #'vertico-directory-enter)
   (define-vertico-key "~"
-    'file #'vertico-directory-home)
+                      'file #'vertico-directory-home)
   (define-vertico-key "DEL"
-    'file #'vertico-directory-delete-char
-    'project-file #'vertico-directory-delete-char)
+                      'file #'vertico-directory-delete-char
+                      'project-file #'vertico-directory-delete-char)
   (define-vertico-key "M-DEL"
-    'file #'vertico-directory-delete-word
-    'project-file #'vertico-directory-delete-word)
+                      'file #'vertico-directory-delete-word
+                      'project-file #'vertico-directory-delete-word)
   :commands (vertico-directory-enter vertico-directory-delete-word vertico-directory-delete-char)
   ;; Tidy shadowed file names
   :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
@@ -152,21 +214,21 @@ DEFS is a plist associating completion categories to commands."
 
 (use-extension vertico vertico-indexed
   :config
-  (defmacro define-choose (n)
+  (defmacro define-vertico-choose (n)
     `(defun ,(intern (format "vertico-indexed-choose-%s" n)) ()
        ,(format "Exit minibuffer with candidate %s." n)
        (interactive)
-       (let ((current-prefix-arg ,n))
+       (let ((vertico--index ,n))
          (funcall-interactively 'vertico-exit))))
-  (defmacro define-insert (n)
+  (defmacro define-vertico-insert (n)
     `(defun ,(intern (format "vertico-indexed-insert-%s" n)) ()
        ,(format "Insert candidate %s in minibuffer." n)
        (interactive)
-       (let ((current-prefix-arg ,n))
+       (let ((vertico--index ,n))
          (funcall-interactively 'vertico-insert))))
   (dotimes (n 10)
-    (eval `(define-choose ,n))
-    (eval `(define-insert ,n))
+    (eval `(define-vertico-choose ,n))
+    (eval `(define-vertico-insert ,n))
     (define-key vertico-map (kbd (format "C-%s" n)) (intern (format "vertico-indexed-choose-%s" n)))
     (define-key vertico-map (kbd (format "M-%s" n)) (intern (format "vertico-indexed-insert-%s" n))))
   (vertico-indexed-mode 1))
@@ -179,32 +241,36 @@ DEFS is a plist associating completion categories to commands."
 (use-package consult
   :bind (;; C-c bindings (mode-specific-map)
          ("C-c h" . consult-history)
-         ("C-c m" . consult-mode-command)
+         ("C-c M-x" . consult-mode-command)
          ("C-c b" . consult-bookmark)
          ("C-c k" . consult-kmacro)
+         ("C-c m" . consult-man)
+         ("C-c i" . consult-info)
+         ([remap Info-search] . consult-info)
          ;; C-x bindings (ctl-x-map)
          ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
          ("C-x b" . consult-buffer) ;; orig. switch-to-buffer
-         ("C-x B" . consult-buffer-no-preview) ;; orig. switch-to-buffer
+         ("C-x B" . consult-buffer-no-preview)
          ("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 t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab
+         ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
+         ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
          ;; 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
+         ("C-," . consult-line)
          ("C-S-s" . consult-line)
-         ("M-*" . consult-line-thing-at-point)
          ("C-c f" . consult-recent-file)
          ("C-c r" . consult-ripgrep)
+         ("C-c ." . consult-ripgrep) ;; convenient for using with embark-act (C-. C-c . to search for thing at point)
          ;; TODO find an alternative to C-c c?
          ("C-c c r" . consult-ripgrep-auto-preview)
          ("C-c c s" . consult-ripgrep-case-sensitive)
-         ("C-c c z" . consult-z-ripgrep)
-         ("C-c C-*" . consult-ripgrep-thing-at-point)
          ("C-c C-^" . consult-ripgrep-parent)
          ("M-y" . consult-yank-pop)     ;; orig. yank-pop
-         ("<help> a" . consult-apropos) ;; orig. apropos-command
          ;; M-g bindings (goto-map)
          ("M-g e" . consult-compile-error)
          ("M-g f" . consult-flycheck)
@@ -227,41 +293,42 @@ DEFS is a plist associating completion categories to commands."
                ("G" . consult-git-grep)
                ("r" . consult-ripgrep)
                ("R" . consult-ripgrep) ;; can't use r in isearch-mode, so add R too
-               ("M-r" . consult-ripgrep-unrestricted)
-               ("*" . consult-ripgrep-thing-at-point)
-               ("z" . consult-z-ripgrep)
+               ("u" . consult-ripgrep-unrestricted)
                ("^" . consult-ripgrep-parent)
                ("l" . consult-line)
                ("L" . consult-line-multi)
                ("m" . consult-multi-occur)
                ("k" . consult-keep-lines)
-               ("u" . consult-focus-lines)
-               ("e" . consult-isearch))
+               ("C-f" . consult-focus-lines)
+               ("e" . consult-isearch-history))
          (:map vertico-map
                ;; These are used for previewing with some consult commands (see consult-customize call below)
                ("C-S-p" . vertico-previous)
                ("C-S-n" . vertico-next)
                ;; Toggle preview on/off without changing preview-key
-               ("M-P" . consult-toggle-preview)))
+               ("M-P" . consult-toggle-preview)
+               ("C-x C-M-x" . remove-leading-hash)))
 
   :config
 
-  ;; Optionally configure the register formatting. This improves the register
+  ;; Configure register formatting. This improves the register
   ;; preview for `consult-register', `consult-register-load',
   ;; `consult-register-store' and the Emacs built-ins.
   (setq register-preview-delay 0
         register-preview-function #'consult-register-format)
 
-  ;; Optionally tweak the register preview window.
+  ;; Tweak the register preview window.
   ;; This adds thin lines, sorting and hides the mode line of the window.
   (advice-add #'register-preview :override #'consult-register-window)
 
-  ;; Use Consult to select xref locations with preview
-  (setq xref-show-xrefs-function #'consult-xref
-        xref-show-definitions-function #'consult-xref)
-
   (add-to-list 'consult-mode-histories '(cider-repl-mode cider-repl-input-history))
 
+  (defvar consult-line-map
+    (let ((map (make-sparse-keymap)))
+      (define-key map "\C-s" #'previous-history-element)
+      map))
+  (consult-customize consult-line :keymap consult-line-map)
+
   (defun consult-ripgrep-auto-preview (&optional dir initial)
     (interactive "P")
     (consult-ripgrep dir initial))
@@ -269,10 +336,6 @@ DEFS is a plist associating completion categories to commands."
     (interactive "P")
     (let ((consult-ripgrep-args (replace-regexp-in-string "\\." "-uu ." consult-ripgrep-args)))
       (consult-ripgrep dir initial)))
-  (defun consult-z-ripgrep (&optional dir initial)
-    (interactive "P")
-    (let ((consult-ripgrep-args (replace-regexp-in-string "\\." "-z ." consult-ripgrep-args)))
-      (consult-ripgrep dir initial)))
   (defun consult-ripgrep-case-sensitive (&optional dir initial)
     (interactive "P")
     (let ((consult-ripgrep-args (replace-regexp-in-string "\\." "-s ." consult-ripgrep-args)))
@@ -282,25 +345,19 @@ DEFS is a plist associating completion categories to commands."
     (consult-buffer))
   (defun consult-ripgrep-parent (&optional initial)
     (interactive "P")
-    (consult-ripgrep (file-name-directory (directory-file-name (persp-current-project-root))) initial))
-
-  (defalias 'consult-line-thing-at-point 'consult-line)
-  (defalias 'consult-ripgrep-thing-at-point 'consult-ripgrep)
+    (consult-ripgrep (file-name-directory
+                      (directory-file-name (or (persp-current-project-root) default-directory)))
+                     initial))
 
   (consult-customize
    consult-theme
    :preview-key '(:debounce 0.2 any)
    ;; For these commands we can use C-N/C-P to scroll and preview, or M-. to preview
    consult-git-grep consult-grep
-   consult-ripgrep-parent consult-ripgrep consult-ripgrep-case-sensitive
-   consult-ripgrep-unrestricted consult-z-ripgrep consult-ripgrep-thing-at-point
+   consult-ripgrep-parent consult-ripgrep consult-ripgrep-case-sensitive consult-ripgrep-unrestricted
    consult-bookmark consult-recent-file consult-xref consult-buffer-no-preview
-   consult--source-recent-file consult--source-project-recent-file consult--source-bookmark
-   :preview-key '("M-." :debounce 0.2 "C-S-n" :debounce 0.2 "C-S-p")
-   consult-ripgrep-thing-at-point
-   :initial (concat "#" (thing-at-point 'symbol))
-   consult-line-thing-at-point
-   :initial (thing-at-point 'symbol))
+   consult--source-recent-file consult--source-project-recent-file consult--source-bookmark;
+   :preview-key '("M-." :debounce 0.2 "C-S-n" :debounce 0.2 "C-S-p"))
 
   (defvar-local consult-toggle-preview-orig nil)
   (defun consult-toggle-preview ()
@@ -398,12 +455,46 @@ DEFS is a plist associating completion categories to commands."
                                          (format  "fd --color never -t f -0 . %s" root))
                                         "\0" t))))))))
 
+  (defun remove-leading-hash ()
+    "Remove a # character from the beginning of the current line.
+
+Designed to be used for consult commands that automatically add a # at the beginning of the minibuffer.
+See `+become' and the functions that call it (e.g. `+become-consult-line')."
+    (interactive)
+    (save-excursion
+      (beginning-of-line)
+      (when (= ?# (char-after))
+        (delete-forward-char 1))))
+
   (defun consult-project-buffer ()
+    "Version of `consult-buffer' that only uses project-related sources."
     (interactive)
     (let ((consult-buffer-sources '(consult--project-source-project-buffer
                                     consult--project-source-project-file-recentf
                                     consult--project-source-project-file-all)))
-      (consult-buffer))))
+      (consult-buffer)))
+
+  ;; https://takeonrules.com/2024/06/08/adding-a-consult-function-for-visualizing-xref/
+  (defvar consult--xref-history nil
+    "History for the `consult-recent-xref' results.")
+  (defun consult-recent-xref (&optional markers)
+    "Jump to a marker in MARKERS list (defaults to `xref--history'.
+
+The command supports preview of the currently selected marker position.
+The symbol at point is added to the future history."
+    (interactive)
+    (consult--read
+     (consult--global-mark-candidates
+      (or markers (flatten-list xref--history)))
+     :prompt "Go to Xref: "
+     :annotate (consult--line-prefix)
+     :category 'consult-location
+     :sort nil
+     :require-match t
+     :lookup #'consult--lookup-location
+     :history '(:input consult--xref-history)
+     :add-history (thing-at-point 'symbol)
+     :state (consult--jump-state))))
 
 (use-package consult-flycheck)
 
@@ -424,6 +515,9 @@ DEFS is a plist associating completion categories to commands."
 (use-package consult-ls-git
   :bind ("C-c g f" . consult-ls-git))
 
+(use-package consult-vc-modified-files
+  :bind (:map vc-prefix-map ("f" . consult-vc-modified-files)))
+
 (use-package consult-project-extra)
 
 (use-package marginalia
@@ -435,19 +529,38 @@ DEFS is a plist associating completion categories to commands."
 (use-package embark
   :bind
   (("C-." . embark-act)
-   ("M-." . embark-dwim)
+   ([remap xref-find-definitions-current-list-function] . embark-dwim-beginning-of-list)
+   ([remap xref-find-definitions] . embark-dwim)
+   ([remap xref-find-definitions-other-window] . embark-dwim-other-window)
    ("C-c C-o" . embark-export)
    ("C-h b" . embark-bindings)
    ("C-h B" . describe-bindings)
    (:map minibuffer-local-map
          ("M-." . embark-preview)
-         ("C-," . embark-become))
+         ("C-," . embark-become)
+         ("C-^" . embark-become-ripgrep-parent)
+         ("C-S-SPC" . embark-select))
    (:map embark-become-file+buffer-map
          ("e" . consult-project-extra-find)
          ("E" . project-switch-consult-project-extra-find)))
   :custom
   (prefix-help-command 'embark-prefix-help-command)
   :config
+  (defun embark-dwim-other-window ()
+    "Like `embark-dwim' but switch to the other window."
+    (interactive)
+    (other-window-prefix)
+    (embark-dwim))
+  (defun embark-dwim-beginning-of-list ()
+    "`embark-dwim' at the beginning of the current list.
+With a prefix argument, moves up `current-prefix-arg' sexps first."
+    (interactive)
+    (progn
+      (when current-prefix-arg
+        (sp-backward-up-sexp current-prefix-arg))
+      (sp-beginning-of-sexp)
+      (embark-dwim)))
+  (defalias 'embark-become-ripgrep-parent (kmacro "C-, ^"))
   (defun embark-preview ()
     "Previews candidate in vertico buffer, unless it's a consult command"
     (interactive)
@@ -467,10 +580,31 @@ DEFS is a plist associating completion categories to commands."
   ;; demand, combined with after means that this will load after embark and consult
   ;; See https://github.com/oantolin/embark/commit/47daded610b245caf01a97d74c940aff91fe14e2#r46010972
   :demand t
+  :config
+  (defun +become (fn)
+    "Remove the leading # from the minibuffer, and call `FN'.
+Useful with embark-become, when changing from a command that uses # as a separator, to one that doesn't."
+    (interactive)
+    (setq unread-command-events (listify-key-sequence "\C-x\C-\M-x"))
+    (funcall-interactively fn))
+  (defun +become-consult-line ()
+    "A version of `consult-line', designed for use with `embark-become'.
+The leading # added by other consult commands is removed."
+    (interactive)
+    (+become #'consult-line))
+  (defun +become-consult-line ()
+    "A version of `consult-imenu', designed for use with `embark-become'.
+The leading # added by other consult commands is removed."
+    (interactive)
+    (+become #'consult-imenu))
   :bind
   (:map embark-consult-async-search-map
+        ("l" . +become-consult-line)
+        ("f" . consult-focus-lines)
+        ("i" . +become-consult-imenu)
         ("^" . consult-ripgrep-parent)
-        ("R" . consult-ripgrep-unrestricted))
+        ("u" . consult-ripgrep-unrestricted)
+        ("c" . consult-ripgrep-case-sensitive))
   :hook
   (embark-collect-mode . consult-preview-at-point-mode))