summaryrefslogtreecommitdiff
path: root/emacs/inits/11-completion.el
blob: aa1253802a57f4368133506bc67b469e88a8df26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
;; Time-stamp: <2026-01-02 16h17 GMT (d63c23ed)>

(use-package savehist
  :ensure t
  :init (savehist-mode))

;; -----------------------------------------------------------------------------
;; General configuration

(use-package emacs
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  (defun crm-indicator (args)
    (cons (concat "[CRM] " (car args)) (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

  ;; Grow and shrink minibuffer
  ;;(setq resize-mini-windows t)

  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)

  ;; Enable recursive minibuffers
  (setq enable-recursive-minibuffers t))

(use-package icomplete
  :hook  ((after-init
           . (lambda () (icomplete-vertical-mode 1)))
          (icomplete-minibuffer-setup
           . (lambda () (setq truncate-lines t))))

  :init
  (defun my-completing-read-in-region (start end collection &optional predicate)
    "Prompt for completion of region in the minibuffer if non-unique.
   Use as a value for `completion-in-region-function'."
    (let* ((initial (buffer-substring-no-properties start end))
           (all (completion-all-completions initial collection predicate
                                            (length initial)))
           (completion (cond
                        ((atom all) nil)
                        ((and (consp all) (atom (cdr all))) (car all))
                        (t (completing-read
                            "Completion: " collection predicate t initial)))))
      (cond (completion (completion--replace start end completion) t)
            (t (message "No completion") nil))))

  (defun my-icomplete-backspace ()
    (interactive)
    (if (looking-back "[^/]/" (line-beginning-position))
        (backward-kill-word 1)
      (delete-backward-char 1)))

  (defun my-switch-to-buffer-with-recentf ()
    "Switch to buffer or recent file with visual separation."
    (interactive)
    (let* ((current-buffer (buffer-name (current-buffer)))
           (buffers (seq-remove
                     (lambda (b) (or (string-prefix-p " " b) (string= b current-buffer)))
                     (mapcar #'buffer-name (buffer-list))))
           (recent-files (seq-take recentf-list 20))
           (separator "─── Recent Files ───")
           (buffer-cands (mapcar (lambda (buf)
                                   (propertize buf 'multi-category (cons 'buffer buf)))
                                 buffers))
           (header-cand (propertize separator 'multi-category (cons 'separator t)))
           (file-cands (mapcar (lambda (file)
                                 (propertize file 'multi-category (cons 'file file)))
                               recent-files))
           (all-cands (append buffer-cands (list header-cand) file-cands))
           (my-table (lambda (string pred action)
                       (pcase action
                         ('metadata '(metadata (category . multi-category) (cycle-sort-function . identity)))
                         ('all all-cands)
                         ('lambda (or (try-completion string all-cands pred) string))
                         (_ (seq-filter (lambda (c) (string-prefix-p string (substring-no-properties c)))
                                        all-cands))))))

      (let ((result (completing-read "Switch to: " my-table nil t)))
        (unless (string-empty-p result)
          (cond
           ((string= result separator) nil)
           ((file-exists-p result) (find-file result))
           (t (switch-to-buffer result)))))))

  :bind (("C-x b" . my-switch-to-buffer-with-recentf)
         (:map icomplete-minibuffer-map
               ("TAB" . icomplete-force-complete)
               ("RET" . icomplete-force-complete-and-exit)
               ("<backspace>" . my-icomplete-backspace)))
  :config
  (setq icomplete-compute-delay 0
        icomplete-show-matches-on-no-input t
        icomplete-hide-common-prefix nil
        icomplete-prospects-height 10
        icomplete-max-delay-chars 0
        icomplete-scroll t)

  (setq completion-styles '(flex basic partial-completion)
        completion-ignore-case t)

  (advice-add 'completion-at-point
              :after #'minibuffer-hide-completions)

  (setq completion-in-region-function #'my-completing-read-in-region))

;; -----------------------------------------------------------------------------
;; Marginalia

(use-package marginalia
  :ensure t
  :bind (:map minibuffer-local-map ("M-A" . marginalia-cycle))
  :init (marginalia-mode))