;; -*- lexical-binding: t; -*-

;; Nicer minibuffer completion

;; First Vertico, which provides the core incremental minibuffer completion engine
(use-package vertico
  :straight (:files (:defaults "extensions/vertico-repeat.el"
                               "extensions/vertico-directory.el"))
  :init
  (vertico-mode))

(use-package vertico-repeat
  :straight (:type built-in)
  :after vertico
  :config
  (add-hook 'minibuffer-setup-hook #'vertico-repeat-save)
  :general
  (leader-map "r" #'vertico-repeat))

(use-package vertico-directory
  :straight (:type built-in)
  :after vertico
  :general
  (vertico-map "RET" #'vertico-directory-enter
               "DEL" #'vertico-directory-delete-char
               "M-DEL" #'vertico-directory-delete-word)
  ;; Tidy shadowed file names
  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

;; Save history in order to persist sort order across emacs sessions
(use-package savehist
  :straight (:type built-in)
  :config
  (savehist-mode 1)
  :custom
  (savehist-additional-variables '(projectile-project-command-history
                                   evil-jumps-history
                                   vertico-repeat-history)))

;; Then orderless, which adds the ability to filter completions in an intuitive way
(use-package orderless
  :ensure t
  :custom
  (completion-styles '(orderless basic))
  (completion-category-overrides '((file (styles basic partial-completion)))))

;; Marginalia adds annotations to completion candidates
(use-package marginalia
  :demand t
  :config
  (marginalia-mode 1)
  (add-to-list 'marginalia-prompt-categories '("Find file:" . project-file))
  (add-to-list 'marginalia-prompt-categories '("Find dir:" . project-file))
  (add-to-list 'marginalia-prompt-categories '("Switch to project" . file))
  (add-to-list 'marginalia-prompt-categories '("recipe\\|package" . straight))
  (add-to-list 'marginalia-prompt-categories '("Password entry" . password-store))
  :general
  (minibuffer-local-map "M-A" #'marginalia-cycle)
  :custom
  (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil)))

;; Embark adds context actions to completion candidates (and other things!)
(use-package embark
  :config
  (defun embark-which-key-indicator ()
    "An embark indicator that displays keymaps using which-key.
The which-key help message will show the type and value of the
current target followed by an ellipsis if there are further
targets."
    (lambda (&optional keymap targets prefix)
      (if (null keymap)
          (which-key--hide-popup-ignore-command)
        (which-key--show-keymap
         (if (eq (caar targets) 'embark-become)
             "Become"
           (format "Act on %s '%s'%s"
                   (plist-get (car targets) :type)
                   (embark--truncate-target (plist-get (car targets) :target))
                   (if (cdr targets) "…" "")))
         (if prefix
             (pcase (lookup-key keymap prefix 'accept-default)
               ((and (pred keymapp) km) km)
               (_ (key-binding prefix 'accept-default)))
           keymap)
         nil nil t))))
  (defvar-keymap embark-straight-map
    :doc "Keymap for actions for straight.el"
    :parent embark-general-map
    "u" #'straight-visit-package-website
    "r" #'straight-get-recipe
    "i" #'straight-use-package
    "c" #'straight-check-package
    "F" #'straight-pull-package
    "f" #'straight-fetch-package
    "p" #'straight-push-package
    "n" #'straight-normalize-package
    "m" #'straight-merge-package)
  (add-to-list 'embark-keymap-alist '(straight . embark-straight-map))
  (defvar-keymap embark-password-store-actions
    :doc "Keymap for actions for password-store."
    :parent embark-general-map
    "c" #'password-store-copy
    "f" #'password-store-copy-field
    "i" #'password-store-insert
    "I" #'password-store-generate
    "r" #'password-store-rename
    "e" #'password-store-edit
    "k" #'password-store-remove
    "U" #'password-store-url)
  (add-to-list 'embark-keymap-alist '(password-store . embark-password-store-actions))
  (add-to-list 'embark-target-injection-hooks '(xref-find-references embark--ignore-target))
  :general
  ((emacs normal motion insert visual) "C-." #'embark-act)
  ((emacs normal motion insert visual) "M-." #'embark-dwim)
  ("C-." #'embark-act)
  ("M-." #'embark-dwim)
  (embark-file-map "s" #'sudo-edit-find-file
                   "l" #'vlf
                   "g" #'magit-file-dispatch)
  ('normal embark-collect-mode-map
           "TAB" #'forward-button
           "?" #'describe-mode
           "A" #'embark-collect-direct-action-minor-mode
           "S" #'tabulated-list-sort
           "a" #'embark-act
           "b" #'backward-button
           "e" #'embark-export
           "f" #'forward-button
           "gr" #'revert-buffer
           "n" #'next-line
           "p" #'previous-line
           "q" #'quit-window
           "s" #'isearch-forward
           "v" #'embark-collect-toggle-view
           "z" #'embark-collect-zebra-minor-mode
           "{" #'tabulated-list-narrow-current-column
           "}" #'tabulated-list-widen-current-column
           "<backtab>" #'backward-button)
  :custom
  (embark-prompter 'embark-keymap-prompter)
  (embark-indicators '(embark-which-key-indicator
                       embark-highlight-indicator
                       embark-isearch-highlight-indicator)))

;; Consult adds a bunch of completing-read based utilities
(use-package consult
  :init
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)
  :custom
  (consult-project-function #'projectile-project-root)
  (consult-find-args "find . -not ( -wholename */.git/* -prune )")
  :general
  ([remap switch-to-buffer] #'consult-buffer)
  ([remap imenu] #'consult-imenu)
  ([remap projectile-ripgrep] #'consult-ripgrep)
  ([remap projectile-grep] #'consult-grep)
  ("C-c p" #'consult-yank-from-kill-ring))

(use-package consult-flycheck
  :after flycheck
  :general
  (flycheck-command-map "!" #'consult-flycheck))

(use-package embark-consult
  :general
  (leader-map "/" embark-consult-search-map))

;; Make grep-like embark collect buffers editable
(use-package wgrep
  :general
  (grep-mode-map "C-x C-q" #'wgrep-change-to-wgrep-mode))

(provide 'init-completion)