summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/ravi-init-completion.el363
1 files changed, 363 insertions, 0 deletions
diff --git a/lisp/ravi-init-completion.el b/lisp/ravi-init-completion.el
new file mode 100644
index 0000000..d6f0d4f
--- /dev/null
+++ b/lisp/ravi-init-completion.el
@@ -0,0 +1,363 @@
+;;; ravi-init-completion.el --- selectrum/prescient/orderless/marginalia/embark/consult -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Ravi R Kiran
+
+;; Author: Ravi R Kiran <aine.marina@gmail.com>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Try out newfangled completion UI
+
+;;; Code:
+
+;; Use selectrum as completion UI with orderless for filtering and
+;; prescient for frecency sorting
+
+(use-package vlf)
+(use-package sudo-edit)
+
+(use-package orderless
+ :if (member ravi/use-selection-system '(selectrum vertico))
+ :custom (completion-styles '(orderless))
+ :config
+ (when (equal ravi/use-selection-system 'selectrum)
+ (setq orderless-skip-highlighting (lambda () selectrum-is-active))))
+
+(use-package selectrum
+ :if (equal ravi/use-selection-system 'selectrum)
+ :config
+ (setq selectrum-highlight-candidates-function #'orderless-highlight-matches)
+ (setq selectrum-max-window-height 22)
+ (selectrum-mode +1)
+
+ (autoload 'ffap-guesser "ffap")
+ (setq minibuffer-default-add-function
+ (defun minibuffer-default-add-function+ ()
+ (with-selected-window (minibuffer-selected-window)
+ (delete-dups
+ (delq nil
+ (list (thing-at-point 'symbol)
+ (thing-at-point 'list)
+ (ffap-guesser)
+ (thing-at-point-url-at-point))))))))
+(use-package selectrum-prescient
+ :if (equal ravi/use-selection-system 'selectrum)
+ :config
+ (setq selectrum-prescient-enable-filtering nil)
+ (selectrum-prescient-mode +1)
+ (prescient-persist-mode +1))
+
+(use-package vertico
+ :if (equal ravi/use-selection-system 'vertico)
+ :init
+ (vertico-mode))
+
+(use-package marginalia
+ :if (member ravi/use-selection-system '(selectrum vertico))
+ :config (marginalia-mode))
+(use-package embark
+ :if (member ravi/use-selection-system '(selectrum vertico))
+ :after (sudo-edit vlf)
+ :commands (embark-act)
+ :bind (:map
+ ;; selectrum-minibuffer-map
+ vertico-map
+ ("C-l" . embark-act)
+ :map
+ embark-file-map
+ ("s" . sudo-edit)
+ ("l" . vlf)
+ ("G" . embark-magit-status))
+ :init
+ ;; Optionally replace the key help with a completing-read interface
+ (setq prefix-help-command #'embark-prefix-help-command)
+ (bind-key "C-l" #'embark-act
+ (pcase ravi/use-selection-system
+ ('selectrum selectrum-minibuffer-map)
+ ('vertico vertico-map)))
+ :config
+ (defun embark-magit-status (file)
+ "Run `magit-status` on repo containing the embark target."
+ (interactive "GFile: ")
+ (magit-status (locate-dominating-file file ".git")))
+
+ ;; Hide the mode line of the Embark live/completions buffers
+ (add-to-list 'display-buffer-alist
+ '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
+ nil
+ (window-parameters (mode-line-format . none)))))
+
+(use-package consult
+ :if (member ravi/use-selection-system '(selectrum vertico))
+ :after (which-key)
+ :bind (;; C-c bindings (mode-specific-map)
+ ("C-c h" . consult-history)
+ ("C-c m" . consult-mode-command)
+ ("C-c b" . consult-bookmark)
+ ("C-c k" . consult-kmacro)
+ ;; 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 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
+ ;; Custom M-# bindings for fast register access
+ ("M-#" . consult-register-load)
+ ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
+ ("C-M-#" . consult-register)
+ ;; Other custom bindings
+ ("M-y" . consult-yank-pop) ;; orig. yank-pop
+ ("<help> a" . consult-apropos) ;; orig. apropos-command
+ ;; M-g bindings (goto-map)
+ ("M-g e" . consult-compile-error)
+ ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
+ ;; Don't use consult-goto-line because avy-goto-line keeps fingers on home row
+ ;; ("M-g g" . consult-goto-line) ;; orig. goto-line
+ ;; ("M-g M-g" . consult-goto-line) ;; orig. goto-line
+ ("M-g o" . consult-outline) ;; Alternative: consult-org-heading
+ ("M-g m" . consult-mark)
+ ("M-g k" . consult-global-mark)
+ ("M-g i" . consult-imenu)
+ ("M-g I" . consult-imenu-multi)
+ ;; M-s bindings (search-map)
+ ("M-s f" . consult-find)
+ ("M-s F" . consult-locate)
+ ("M-s g" . consult-grep)
+ ("M-s G" . consult-git-grep)
+ ;; ("M-s r" . consult-ripgrep)
+ ("M-i" . consult-line)
+ ("M-s l" . consult-line)
+ ("M-s L" . consult-line-multi)
+ ("M-s m" . consult-multi-occur)
+ ("M-s k" . consult-keep-lines)
+ ("M-s u" . consult-focus-lines)
+ ;; Isearch integration
+ ("M-s e" . consult-isearch)
+ :map isearch-mode-map
+ ("M-e" . consult-isearch) ;; orig. isearch-edit-string
+ ("M-s e" . consult-isearch) ;; orig. isearch-edit-string
+ ("M-s l" . consult-line) ;; needed by consult-line to detect isearch
+ ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
+ :map consult-narrow-map
+ ("C-r" . consult-narrow-cycle-backward)
+ ("C-s" . consult-narrow-cycle-forward))
+
+ ;; Enable automatic preview at point in the *Completions* buffer.
+ ;; This is relevant when you use the default completion UI,
+ ;; and not necessary for Vertico, Selectrum, etc.
+ ;; :hook (completion-list-mode . consult-preview-at-point-mode)
+
+ ;; The :init configuration is always executed (Not lazy)
+ :init
+
+ ;; Optionally configure the 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.
+ ;; This adds thin lines, sorting and hides the mode line of the window.
+ (advice-add #'register-preview :override #'consult-register-window)
+
+ ;; Optionally replace `completing-read-multiple' with an enhanced version.
+ (advice-add #'completing-read-multiple :override #'consult-completing-read-multiple)
+
+ ;; Use Consult to select xref locations with preview
+ (setq xref-show-xrefs-function #'consult-xref
+ xref-show-definitions-function #'consult-xref)
+
+ ;; Configure other variables and modes in the :config section,
+ ;; after lazily loading the package.
+ :config
+
+ ;; Optionally configure preview. The default value
+ ;; is 'any, such that any key triggers the preview.
+ ;; (setq consult-preview-key 'any)
+ (setq consult-preview-key (kbd "C-o"))
+ ;; (setq consult-preview-key (list (kbd "<S-down>") (kbd "<S-up>")))
+ ;; For some commands and buffer sources it is useful to configure the
+ ;; :preview-key on a per-command basis using the `consult-customize' macro.
+ (consult-customize
+ consult-theme
+ :preview-key '(:debounce 0.2 any)
+ consult-ripgrep consult-git-grep consult-grep consult-xref
+ :preview-key 'any
+ consult-bookmark consult-recent-file
+ consult--source-file consult--source-project-file consult--source-bookmark
+ :preview-key "C-o")
+ (consult-customize consult-buffer :preview-key nil) ; preview is just too intrusive
+
+ ;; Optionally configure the narrowing key.
+ ;; Both < and C-+ work reasonably well.
+ (setq consult-narrow-key "<") ;; (kbd "C-+")
+
+ ;; Optionally make narrowing help available in the minibuffer.
+ ;; You may want to use `embark-prefix-help-command' or which-key instead.
+ (define-key consult-narrow-map (vconcat consult-narrow-key "?") #'consult-narrow-help)
+
+ ;; Optionally configure a function which returns the project root directory.
+ ;; There are multiple reasonable alternatives to chose from.
+ ;;;; 1. project.el (project-roots)
+ ;; (setq consult-project-root-function
+ ;; (lambda ()
+ ;; (when-let (project (project-current))
+ ;; (car (project-roots project)))))
+ ;;;; 2. projectile.el (projectile-project-root)
+ (autoload 'projectile-project-root "projectile")
+ (setq consult-project-root-function #'projectile-project-root)
+ ;;;; 3. vc.el (vc-root-dir)
+ ;; (setq consult-project-root-function #'vc-root-dir)
+ ;;;; 4. locate-dominating-file
+ ;; (setq consult-project-root-function (lambda () (locate-dominating-file "." ".git")))
+
+ ;; First line as default is entirely useless
+ (consult-customize
+ consult-line consult-line-multi
+ :default nil)
+
+ ;; Replace by embark-prefix-help-command?
+ (defun immediate-which-key-for-narrow (fun &rest args)
+ (let* ((refresh t)
+ (timer (and consult-narrow-key
+ (memq :narrow args)
+ (run-at-time 0.05 0.05
+ (lambda ()
+ (if (eq last-input-event (elt consult-narrow-key 0))
+ (when refresh
+ (setq refresh nil)
+ (which-key--update))
+ (setq refresh t)))))))
+ (unwind-protect
+ (apply fun args)
+ (when timer
+ (cancel-timer timer)))))
+ (advice-add #'consult--read :around #'immediate-which-key-for-narrow)
+
+ (defun consult-narrow-cycle-backward ()
+ "Cycle backward through the narrowing keys."
+ (interactive)
+ (when consult--narrow-keys
+ (consult-narrow
+ (if consult--narrow
+ (let ((idx (seq-position consult--narrow-keys
+ (assq consult--narrow consult--narrow-keys))))
+ (unless (eq idx 0)
+ (car (nth (1- idx) consult--narrow-keys))))
+ (caar (last consult--narrow-keys))))))
+
+ (defun consult-narrow-cycle-forward ()
+ "Cycle forward through the narrowing keys."
+ (interactive)
+ (when consult--narrow-keys
+ (consult-narrow
+ (if consult--narrow
+ (let ((idx (seq-position consult--narrow-keys
+ (assq consult--narrow consult--narrow-keys))))
+ (unless (eq idx (1- (length consult--narrow-keys)))
+ (car (nth (1+ idx) consult--narrow-keys))))
+ (caar consult--narrow-keys)))))
+ )
+
+(use-package embark-consult
+ :if (member ravi/use-selection-system '(selectrum vertico))
+ :after (embark consult)
+ :demand t ; only necessary if you have the hook below
+ ;; if you want to have consult previews as you move around an
+ ;; auto-updating embark collect buffer
+ :hook
+ (embark-collect-mode . consult-preview-at-point-mode))
+
+(use-package dash-docs
+ :if (member ravi/use-selection-system '(selectrum vertico))
+ :bind (("M-s d" . 'ravi/dash)
+ ("M-s D" . 'ravi/dash-at-point))
+ :config
+ (require 'cl-lib) ; for cl-remove-duplicates, cl-find-if
+ (require 'subr-x) ; for when-let
+
+ (defvar ravi/dash-history-input nil)
+ (defvar ravi/dash--results nil
+ "Stores the previously retrieved docset results")
+ (defvar-local ravi/dash-docsets nil
+ "Docsets to use for this buffer")
+
+ (advice-add #'dash-docs-buffer-local-docsets :around
+ (lambda (old-fun &rest args)
+ (let ((old (apply old-fun args)))
+ (cl-remove-duplicates (append old ravi/dash-docsets)))))
+
+ (defun ravi/dash--collection (s &rest _)
+ "Given a string S, query docsets and retrieve result."
+ (message "Trying to search for: %s" (prin1-to-string s))
+ (setq ravi/dash--results (dash-docs-search s))
+ (mapcar 'car ravi/dash--results))
+
+ (defun ravi/dash--browse-matching-result (match)
+ "Given a MATCH, find matching result and browse it's url."
+ (when-let ((result
+ (cdr (cl-find-if (lambda (e)
+ (string= match (car e))) ravi/dash--results))))
+ (dash-docs-browse-url result)))
+
+ ;; The following does not work, because vertico and selectrum don't support dynamic completions
+ (defun ravi/dash (&optional initial)
+ "Query dash docsets.
+INITIAL will be used as the initial input, if given."
+ (interactive)
+ (dash-docs-initialize-debugging-buffer)
+ (dash-docs-create-buffer-connections)
+ (dash-docs-create-common-connections)
+ (if t
+ (ravi/dash--browse-matching-result
+ (let ((cb (current-buffer)))
+ (completing-read
+ "Documentation for: "
+ ;; (completion-table-dynamic (lambda (s) (with-current-buffer cb (ravi/dash--collection s))))
+ (completion-table-dynamic #'ravi/dash--collection t)
+ nil ; predicate
+ t ; require-match
+ nil ; initial-input
+ ravi/dash-history-input ; history
+ (when-let ((sym (thing-at-point 'symbol))) (substring-no-properties sym))))) ; default, a.k.a future history
+ (message "%s" (prin1-to-string (ravi/dash--collection "C++ throw")))))
+
+ (defun ravi/dash-at-point-what-it-should-be ()
+ "Bring up a `ravi/dash' search interface with symbol at point."
+ (interactive)
+ (ravi/dash
+ (substring-no-properties (or (thing-at-point 'symbol) ""))))
+
+ (defun ravi/dash-at-point ()
+ "Bring up a `ravi/dash' search interface with symbol at point."
+ (interactive)
+ (dash-docs-initialize-debugging-buffer)
+ (dash-docs-create-buffer-connections)
+ (dash-docs-create-common-connections)
+ (if-let* ((sym (thing-at-point 'symbol))
+ (sym-only (substring-no-properties sym))
+ (cb (current-buffer))
+ (table (with-current-buffer cb (ravi/dash--collection sym-only)))
+ (result (completing-read (format "Documentation for '%s':" sym-only)
+ table nil t nil ravi/dash-history-input)))
+ (progn
+ (ravi/dash--browse-matching-result result)
+ (add-to-list 'ravi/dash-history-input sym-only))
+ (user-error "No symbol at point or documentation not found")))
+ )
+
+(provide 'ravi-init-completion)
+;;; ravi-init-completion.el ends here