Minor doc fix
[emacs-capf-autosuggest.git] / capf-autosuggest.el
blob82bc81ab00d9485274ff2dcbd422519f5afbcc67
1 ;;; capf-autosuggest.el --- History autosuggestions for comint and eshell -*- lexical-binding: t; -*-
3 ;; Filename: capf-autosuggest.el
4 ;; Description: History autosuggestions for comint and eshell
5 ;; Author: jakanakaevangeli <jakanakaevangeli@chiru.no>
6 ;; Created: 2021-07-13
7 ;; Version: 1.0
8 ;; URL: https://github.com/jakanakaevangeli/emacs-capf-autosuggest
10 ;; This file is not part of GNU Emacs.
12 ;; This program is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
25 ;;; Commentary:
27 ;; capf-autosuggest lets you preview the most recent matching history element,
28 ;; similar to zsh-autosuggestions or fish. It works in eshell and in modes
29 ;; derived from comint-mode, for example M-x shell and M-x run-python.
31 ;; Installation:
33 ;; Add the following to your Emacs init file:
35 ;; (add-to-list 'load-path "/path/to/emacs-capf-autosuggest")
36 ;; (require 'capf-autosuggest)
37 ;; (add-hook 'comint-mode-hook #'capf-autosuggest-setup-comint)
38 ;; (add-hook 'eshell-mode-hook #'capf-autosuggest-setup-eshell)
40 ;; Configuration:
42 ;; Use `capf-autosuggest-define-partial-accept-cmd' to make a command that can
43 ;; move point into an auto-suggested layer.
45 ;; Example: to make C-M-f (forward-sexp) movable into suggested text, put the
46 ;; following into you init file:
48 ;; (with-eval-after-load 'capf-autosuggest
49 ;; (capf-autosuggest-define-partial-accept-cmd
50 ;; movable-forward-sexp forward-sexp)
51 ;; (define-key capf-autosuggest-active-mode-map [remap forward-sexp]
52 ;; #'movable-forward-sexp))
54 ;; By default, C-n (next-line) will accept the currently displayed suggestion
55 ;; and send input to shell/eshell. See the customization group
56 ;; capf-autosuggest do disable this behaviour or enable it for other commands,
57 ;; such as C-c C-n or M-n.
59 ;; Details:
61 ;; capf-autosuggest provides a minor mode, capf-autosuggest-mode, that lets you
62 ;; preview the first completion candidate for in-buffer completion as an
63 ;; overlay. Instead of using the default hook `completion-at-point-functions',
64 ;; it uses its own hook `capf-autosuggest-capf-functions'. However, by
65 ;; default, this hook contains a function that reads the default hook, but only
66 ;; if point is at end of line, because an auto-suggested overlay can be
67 ;; annoying in the middle of a line. If you want, you can try enabling this
68 ;; minor mode in an ordinary buffer for previewing tab completion candidates at
69 ;; end of line.
71 ;; completion-at-point functions for comint and eshell history are also
72 ;; provided. Because they are less useful for tab completion and more useful
73 ;; for auto-suggestion preview, they should be added to
74 ;; `capf-autosuggest-capf-functions', which doesn't interfere with tab
75 ;; completion. The setup functions mentioned in "Installation" paragraph add
76 ;; them to this hook locally. By default, if there are no matches for history
77 ;; completion and point is at end of line, we fall back to previewing the
78 ;; default tab completion candidates, as described in the previous paragraph.
80 ;; You can customize this behaviour by customizing
81 ;; `capf-autosuggest-capf-functions'. For example, you could add
82 ;; `capf-autosuggest-orig-capf' to enable auto-suggestions of tab completion
83 ;; candidates in the middle of a line.
85 ;; Alternatives:
87 ;; There is also esh-autosuggest[1] with similar functionality. Differences:
88 ;; it is simpler and more concise, however it depends on company. It optionally
89 ;; allows having a delay and it is implemented only for eshell.
91 ;; [1]: http://github.com/dieggsy/esh-autosuggest
93 ;;; Code:
95 (require 'ring)
96 (eval-when-compile
97 (require 'subr-x)
98 (require 'cl-lib))
100 (defvar comint-input-ring)
101 (defvar comint-accum-marker)
102 (defvar comint-use-prompt-regexp)
103 (defvar eshell-history-ring)
104 (defvar eshell-last-output-end)
105 (declare-function eshell-bol "esh-mode")
106 (declare-function comint-previous-matching-input-from-input "comint")
107 (declare-function eshell-previous-matching-input-from-input "em-hist")
108 (declare-function eshell-send-input "esh-mode")
109 (declare-function eshell-interactive-process "esh-cmd")
110 (declare-function eshell-next-prompt "em-prompt")
111 (declare-function eshell-next-input "em-hist")
112 (declare-function eshell-next-matching-input-from-input "em-hist")
114 (defgroup capf-autosuggest nil
115 "Show completion-at-point as an overlay."
116 :group 'completion
117 :prefix "capf-autosuggest-"
118 :link
119 '(url-link "https://github.com/jakanakaevangeli/emacs-capf-autosuggest"))
121 ;;; Auto-suggestion overlay
123 (defface capf-autosuggest-face '((t :inherit file-name-shadow))
124 "Face used for auto suggestions.")
126 (defvar capf-autosuggest-capf-functions '(capf-autosuggest-orig-if-at-eol-capf)
127 "`completion-at-point-functions', used by capf-autosuggest.
128 It is used instead of the standard
129 `completion-at-point-functions', but the default value contains
130 `capf-autosuggest-orig-if-at-eol-capf' which searches the
131 standard capf functions, if point is at the end of line.")
133 (defvar capf-autosuggest-all-completions-only-one nil
134 "Non-nil if only the first result of `all-completions' is of interest.
135 capf-autosuggest binds this to t around calls to
136 `all-completions'. A dynamic completion table can take this as a
137 hint to only return a list of one element for optimization.")
139 (defvar-local capf-autosuggest--overlay nil)
140 (defvar-local capf-autosuggest--str "")
141 (defvar-local capf-autosuggest--tick nil)
142 (defvar-local capf-autosuggest--region '(nil)
143 "Region of `completion-at-point'.")
145 (defun capf-autosuggest-orig-capf (&optional capf-functions)
146 "A capf that chooses from hook variable CAPF-FUNCTIONS.
147 CAPF-FUNCTIONS defaults to `completion-at-point-functions'.
148 Don't add this function to `completion-at-point-functions', as
149 this will result in an infinite loop. Useful for adding to
150 `capf-autosuggest-capf-functions', making it search the standard
151 capf functions."
152 (cdr (run-hook-wrapped (or capf-functions 'completion-at-point-functions)
153 #'completion--capf-wrapper 'all)))
155 (defun capf-autosuggest-orig-if-at-eol-capf ()
156 "`capf-autosuggest-orig-capf' if at the end of line.
157 Otherwise, return nil."
158 (when (eolp)
159 (capf-autosuggest-orig-capf)))
161 (defvar capf-autosuggest-active-mode)
163 (defun capf-autosuggest--post-h ()
164 "Create an auto-suggest overlay."
165 (when capf-autosuggest-active-mode
166 ;; `identity' is used to generate slightly faster byte-code
167 (pcase-let ((`(,beg . ,end) (identity capf-autosuggest--region)))
168 (unless (and (< beg (point) end)
169 (eq (buffer-modified-tick) capf-autosuggest--tick))
170 (capf-autosuggest-active-mode -1))))
172 (unless capf-autosuggest-active-mode
173 (pcase (let* ((catch-sym (make-symbol "cirf-catch"))
174 ;; `pcomplete-completions-at-point' may illegally use
175 ;; `completion-in-region' itself instead of returning a
176 ;; collection. Let's try to outsmart it.
177 (completion-in-region-function
178 (lambda (start end collection predicate)
179 (throw catch-sym
180 (list start end collection :predicate predicate))))
181 (buffer-read-only t))
182 (condition-case _
183 (catch catch-sym
184 (capf-autosuggest-orig-capf 'capf-autosuggest-capf-functions))
185 (buffer-read-only t)))
186 (`(,beg ,end ,table . ,plist)
187 (let* ((pred (plist-get plist :predicate))
188 (string (buffer-substring-no-properties beg end))
189 ;; See `completion-emacs21-all-completions'
190 (base (car (completion-boundaries string table pred ""))))
191 (when-let*
192 ((completions
193 (let ((capf-autosuggest-all-completions-only-one t))
194 ;; Use `all-completions' rather than
195 ;; `completion-all-completions' to bypass completion styles
196 ;; and strictly match only on prefix. This makes sense here
197 ;; as we only use the string without the prefix for the
198 ;; overlay.
199 (all-completions string table pred)))
200 ;; `all-completions' may return strings that don't strictly
201 ;; match on our prefix. Ignore them.
202 ((string-prefix-p (substring string base) (car completions)))
203 (str (substring (car completions) (- end beg base)))
204 (len (length str))
205 ((/= 0 len)))
206 (setq capf-autosuggest--region (cons beg end)
207 capf-autosuggest--str (copy-sequence str)
208 capf-autosuggest--tick (buffer-modified-tick))
209 (move-overlay capf-autosuggest--overlay end end)
210 ;; Make sure the overlay after-string doesn't start or end with a
211 ;; newline, otherwise it can behave badly with cursor placement
212 (when (eq ?\n (aref str 0))
213 (setq str (concat " " str))
214 (setq len (1+ len)))
215 (when (eq ?\n (aref str (1- len)))
216 (setq str (concat str (propertize " " 'display "")))
217 (setq len (1+ len)))
218 (put-text-property 0 1 'cursor len str)
219 (put-text-property 0 len 'face 'capf-autosuggest-face str)
220 (overlay-put capf-autosuggest--overlay 'after-string str)
221 (capf-autosuggest-active-mode)))))))
223 ;;;###autoload
224 (define-minor-mode capf-autosuggest-mode
225 "Auto-suggest first completion at point with an overlay."
226 :group 'capf-autosuggest
227 (if capf-autosuggest-mode
228 (progn
229 (setq capf-autosuggest--overlay (make-overlay (point) (point) nil t t))
230 (add-hook 'post-command-hook #'capf-autosuggest--post-h nil t)
231 (add-hook 'change-major-mode-hook
232 #'capf-autosuggest-active-mode-deactivate nil t))
233 (remove-hook 'change-major-mode-hook
234 #'capf-autosuggest-active-mode-deactivate t)
235 (remove-hook 'post-command-hook #'capf-autosuggest--post-h t)
236 (capf-autosuggest-active-mode -1)))
238 ;;; Various commands and menu-items
240 ;;;###autoload
241 (defmacro capf-autosuggest-define-partial-accept-cmd (name command)
242 "Define a command NAME.
243 It will call COMMAND interactively, allowing it to move point
244 into an auto-suggested overlay. COMMAND must not modify buffer.
245 NAME must not be called if variable
246 `capf-autosuggest-active-mode' is inactive. NAME is suitable for
247 binding in `capf-autosuggest-active-mode-map'."
248 `(defun ,name ()
249 ,(format "`%s', possibly moving point into an auto-suggested overlay."
250 command)
251 (interactive)
252 (capf-autosuggest-call-partial-accept-cmd #',command)))
254 (defun capf-autosuggest-call-partial-accept-cmd (command)
255 "Call COMMAND interactively, stepping into auto-suggested overlay.
256 Temporarily convert the overlay to buffer text and call COMMAND
257 interactively. Afterwards, the added text is deleted, but only
258 the portion after point. Additionally, if point is outside of
259 the added text, the whole text is deleted."
260 (let (beg end text)
261 (with-silent-modifications
262 (catch 'cancel-atomic-change
263 (atomic-change-group
264 (save-excursion
265 (goto-char (overlay-start capf-autosuggest--overlay))
266 (setq beg (point))
267 (insert-and-inherit capf-autosuggest--str)
268 (setq end (point)))
269 (call-interactively command)
270 (and (> (point) beg)
271 (<= (point) end)
272 (setq text (buffer-substring beg (point))))
273 (throw 'cancel-atomic-change nil))))
274 (when text
275 (if (= (point) beg)
276 (insert text)
277 (save-excursion
278 (goto-char beg)
279 (insert text))))))
281 (declare-function evil-forward-char "ext:evil-commands" nil t)
282 (declare-function evil-end-of-line "ext:evil-commands" nil t)
283 (declare-function evil-end-of-visual-line "ext:evil-commands" nil t)
284 (declare-function evil-end-of-line-or-visual-line "ext:evil-commands" nil t)
285 (declare-function evil-middle-of-visual-line "ext:evil-commands" nil t)
286 (declare-function evil-last-non-blank "ext:evil-commands" nil t)
287 (declare-function evil-forward-word-begin "ext:evil-commands" nil t)
288 (declare-function evil-forward-word-end "ext:evil-commands" nil t)
289 (declare-function evil-forward-WORD-begin "ext:evil-commands" nil t)
290 (declare-function evil-forward-WORD-end "ext:evil-commands" nil t)
292 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-forward-word forward-word)
293 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-forward-char forward-char)
294 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-forward-sexp forward-sexp)
295 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-end-of-line end-of-line)
296 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-move-end-of-line move-end-of-line)
297 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-end-of-visual-line end-of-visual-line)
298 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-forward-char evil-forward-char)
299 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-end-of-line evil-end-of-line)
300 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-end-of-visual-line evil-end-of-visual-line)
301 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-end-of-line-or-visual-line evil-end-of-line-or-visual-line)
302 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-middle-of-visual-line evil-middle-of-visual-line)
303 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-last-non-blank evil-last-non-blank)
304 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-forward-word-begin evil-forward-word-begin)
305 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-forward-word-end evil-forward-word-end)
306 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-forward-WORD-begin evil-forward-WORD-begin)
307 (capf-autosuggest-define-partial-accept-cmd capf-autosuggest-evil-forward-WORD-end evil-forward-WORD-end)
309 (defun capf-autosuggest-accept ()
310 "Accept current auto-suggestion.
311 Do not call this command if variable `capf-autosuggest-active-mode' is
312 inactive."
313 (interactive)
314 (capf-autosuggest-call-partial-accept-cmd
315 (lambda ()
316 (interactive)
317 (goto-char (overlay-start capf-autosuggest--overlay)))))
319 (defun capf-autosuggest-comint-previous-matching-input-from-input (n)
320 "Like `comint-previous-matching-input-from-input'.
321 If N is positive, the command isn't repeated and point is at
322 auto-suggested overlay, increase argument N by 1. This is done
323 in order to skip the history element already shown by the overlay."
324 (interactive "p")
325 (and (not (memq last-command '(comint-previous-matching-input-from-input
326 comint-next-matching-input-from-input)))
327 (> n 0)
328 (= (point) (overlay-start capf-autosuggest--overlay))
329 (setq n (1+ n)))
330 (comint-previous-matching-input-from-input n)
331 (setq this-command #'comint-previous-matching-input-from-input))
333 (defun capf-autosuggest-eshell-previous-matching-input-from-input (n)
334 "Like `eshell-previous-matching-input-from-input'.
335 If N is positive, the command isn't repeated and point is at
336 auto-suggested overlay, increase argument N by 1. This is done
337 in order to skip the history element already shown by the overlay."
338 (interactive "p")
339 (and (not (memq last-command '(eshell-previous-matching-input-from-input
340 eshell-next-matching-input-from-input)))
341 (> n 0)
342 (= (point) (overlay-start capf-autosuggest--overlay))
343 (setq n (1+ n)))
344 (eshell-previous-matching-input-from-input n)
345 (setq this-command #'eshell-previous-matching-input-from-input))
347 (defun capf-autosuggest-comint-send-input ()
348 "`capf-autosuggest-accept' and `comint-send-input'."
349 (interactive)
350 (capf-autosuggest-accept)
351 (call-interactively (or (command-remapping #'comint-send-input)
352 #'comint-send-input))
353 (setq this-command #'comint-send-input))
355 (defun capf-autosuggest-eshell-send-input ()
356 "`capf-autosuggest-accept' and `eshell-send-input'."
357 (interactive)
358 (capf-autosuggest-accept)
359 (call-interactively (or (command-remapping #'eshell-send-input)
360 #'eshell-send-input))
361 (setq this-command #'eshell-send-input))
363 (defun capf-autosuggest-minibuffer-send-input ()
364 "`capf-autosuggest-accept' and `exit-minibuffer'."
365 (interactive)
366 (capf-autosuggest-accept)
367 (call-interactively (or (command-remapping #'exit-minibuffer)
368 #'exit-minibuffer))
369 (setq this-command #'exit-minibuffer))
371 (defcustom capf-autosuggest-dwim-next-line t
372 "Whether `next-line' can accept and send current suggestion.
373 If t and point is on last line, `next-line' will accept the
374 current suggestion and send input."
375 :type 'boolean)
376 (defcustom capf-autosuggest-dwim-next-prompt nil
377 "Whether next-prompt commands can send current suggestion.
378 If t and point is after the last prompt, `comint-next-prompt' and
379 `eshell-next-prompt' will accept the current suggestion and send
380 input."
381 :type 'boolean)
382 (defcustom capf-autosuggest-dwim-next-input nil
383 "Whether next-input commands can send current suggestion.
384 If t and previous command wasn't a history command
385 (next/previous-input or previous/next-matching-input-from-input),
386 `comint-next-input' and `eshell-next-input' will accept the
387 current suggestion and send input."
388 :type 'boolean)
389 (defcustom capf-autosuggest-dwim-next-matching-input-from-input nil
390 "Whether next-input commands can send current suggestion.
391 If t and previous command wasn't a history matching command
392 (previous or next-matching-input-from-input),
393 `comint-next-matching-input-from-input' and
394 `eshell-next-matching-input-from-input' will accept the current
395 suggestion and send input."
396 :type 'boolean)
398 (defvar capf-autosuggest-active-mode-map
399 (let ((map (make-sparse-keymap)))
400 (define-key map [remap forward-word] #'capf-autosuggest-forward-word)
401 (define-key map [remap forward-char] #'capf-autosuggest-forward-char)
402 (define-key map [remap forward-sexp] #'capf-autosuggest-forward-sexp)
403 (define-key map [remap end-of-line] #'capf-autosuggest-end-of-line)
404 (define-key map [remap move-end-of-line] #'capf-autosuggest-move-end-of-line)
405 (define-key map [remap end-of-visual-line] #'capf-autosuggest-end-of-visual-line)
407 (define-key map [remap evil-forward-char] #'capf-autosuggest-evil-forward-char)
408 (define-key map [remap evil-end-of-line] #'capf-autosuggest-evil-end-of-line)
409 (define-key map [remap evil-end-of-visual-line] #'capf-autosuggest-evil-end-of-visual-line)
410 (define-key map [remap evil-end-of-line-or-visual-line] #'capf-autosuggest-evil-end-of-line-or-visual-line)
411 (define-key map [remap evil-middle-of-visual-line] #'capf-autosuggest-evil-middle-of-visual-line)
412 (define-key map [remap evil-last-non-blank] #'capf-autosuggest-evil-last-non-blank)
413 (define-key map [remap evil-forward-word-begin] #'capf-autosuggest-evil-forward-word-begin)
414 (define-key map [remap evil-forward-word-end] #'capf-autosuggest-evil-forward-word-end)
415 (define-key map [remap evil-forward-WORD-begin] #'capf-autosuggest-evil-forward-WORD-begin)
416 (define-key map [remap evil-forward-WORD-end] #'capf-autosuggest-evil-forward-WORD-end)
418 (define-key map [remap eshell-previous-matching-input-from-input]
419 #'capf-autosuggest-eshell-previous-matching-input-from-input)
420 (define-key map [remap comint-previous-matching-input-from-input]
421 #'capf-autosuggest-comint-previous-matching-input-from-input)
423 (define-key map [remap next-line]
424 (list 'menu-item "" #'next-line :filter
425 (lambda (cmd)
426 (if (and capf-autosuggest-dwim-next-line
427 (looking-at-p "[^\n]*\n?\\'"))
428 (cond
429 ((derived-mode-p 'comint-mode) #'capf-autosuggest-comint-send-input)
430 ((derived-mode-p 'eshell-mode) #'capf-autosuggest-eshell-send-input)
431 ((minibufferp) #'capf-autosuggest-minibuffer-send-input)
432 (t cmd))
433 cmd))))
434 (define-key map [remap comint-next-prompt]
435 (list 'menu-item "" #'comint-next-prompt :filter
436 (lambda (cmd)
437 (if (and capf-autosuggest-dwim-next-prompt
438 (comint-after-pmark-p))
439 #'capf-autosuggest-comint-send-input
440 cmd))))
441 (define-key map [remap eshell-next-prompt]
442 (list 'menu-item "" #'eshell-next-prompt :filter
443 (lambda (cmd)
444 (if (and capf-autosuggest-dwim-next-prompt
445 (>= (point) eshell-last-output-end))
446 #'capf-autosuggest-comint-send-input
447 cmd))))
448 (define-key map [remap comint-next-input]
449 (list 'menu-item "" #'comint-next-input :filter
450 (lambda (cmd)
451 (if (or (not capf-autosuggest-dwim-next-input)
452 (memq last-command
453 '(comint-next-matching-input-from-input
454 comint-previous-matching-input-from-input
455 comint-next-input comint-previous-input)))
457 #'capf-autosuggest-comint-send-input))))
458 (define-key map [remap eshell-next-input]
459 (list 'menu-item "" #'eshell-next-input :filter
460 (lambda (cmd)
461 (if (or (not capf-autosuggest-dwim-next-input)
462 (memq last-command
463 '(eshell-next-matching-input-from-input
464 eshell-previous-matching-input-from-input
465 eshell-next-input eshell-previous-input)))
467 #'capf-autosuggest-eshell-send-input))))
468 (define-key map [remap comint-next-matching-input-from-input]
469 (list 'menu-item "" #'comint-next-matching-input-from-input :filter
470 (lambda (cmd)
471 (if (or (not capf-autosuggest-dwim-next-matching-input-from-input)
472 (memq last-command
473 '(comint-next-matching-input-from-input
474 comint-previous-matching-input-from-input)))
476 #'capf-autosuggest-comint-send-input))))
477 (define-key map [remap eshell-next-matching-input-from-input]
478 (list 'menu-item "" #'eshell-next-matching-input-from-input :filter
479 (lambda (cmd)
480 (if (or (not capf-autosuggest-dwim-next-matching-input-from-input)
481 (memq last-command
482 '(eshell-previous-matching-input-from-input
483 eshell-next-matching-input-from-input)))
485 #'capf-autosuggest-eshell-send-input))))
486 map)
487 "Keymap active when an auto-suggestion is shown.")
489 (define-minor-mode capf-autosuggest-active-mode
490 "Active when auto-suggested overlay is shown."
491 :group 'capf-autosuggest
492 (unless capf-autosuggest-active-mode
493 (delete-overlay capf-autosuggest--overlay)))
495 (defun capf-autosuggest-active-mode-deactivate ()
496 "Deactivate `capf-autosuggest-active-mode'."
497 (capf-autosuggest-active-mode -1))
499 ;;;; History completion function
501 ;;;###autoload
502 (defun capf-autosuggest-comint-capf ()
503 "Completion-at-point function for comint input history.
504 Is only applicable if point is after the last prompt."
505 (let ((ring comint-input-ring)
506 (beg nil) (end nil))
507 (or (and (setq beg comint-accum-marker)
508 (setq beg (marker-position beg)))
509 (and (setq beg (get-buffer-process (current-buffer)))
510 (setq beg (marker-position (process-mark beg)))))
511 (cond
512 ;; Return nil to allow possible further capf functions
513 ((null beg) nil)
514 ((< (point) beg) nil)
515 ((<= (setq end (if comint-use-prompt-regexp
516 (line-end-position)
517 (field-end)))
518 beg)
519 ;; Return non-nil but empty completion table to block possible further
520 ;; capf functions
521 (list (point) (point) nil))
522 ((and (ring-p ring) (not (ring-empty-p ring)))
523 (list beg end (capf-autosuggest--completion-table ring)
524 :exclusive 'no)))))
526 (defun capf-autosuggest-eshell-capf ()
527 "Completion-at-point function for eshell input history.
528 Is only applicable if point is after the last prompt."
529 (let ((ring eshell-history-ring)
530 (beg (save-excursion (eshell-bol) (point)))
531 (end (point-max)))
532 (cond
533 ((< (point) eshell-last-output-end) nil)
534 ((< (point) beg) nil)
535 ((and (= end beg) (eshell-interactive-process))
536 (list (point) (point) nil))
537 ((and (ring-p ring) (not (ring-empty-p ring)))
538 (list beg end (capf-autosuggest--completion-table ring)
539 :exclusive 'no)))))
541 (defun capf-autosuggest--completion-table (ring)
542 "Return a completion table to complete on RING."
543 (let (self)
544 (setq
545 self
546 (lambda (input predicate action)
547 (cond
548 ((eq action t)
549 (cl-loop
550 with only-one = capf-autosuggest-all-completions-only-one
551 with regexps = completion-regexp-list
552 for i below (ring-size ring)
553 for elem = (ring-ref ring i)
554 if (string-prefix-p input elem)
555 if (cl-loop for regex in regexps
556 always (string-match-p regex elem))
557 if (or (null predicate)
558 (funcall predicate elem))
559 if only-one
560 return (list elem)
561 else collect elem))
562 ((eq action nil)
563 (complete-with-action
564 nil (let ((capf-autosuggest-all-completions-only-one nil))
565 (funcall self input predicate t))
566 input predicate))
567 ((eq action 'lambda)
568 (and (ring-member ring input)
569 (or (null predicate)
570 (funcall predicate input))
571 (cl-loop for regex in completion-regexp-list
572 always (string-match-p regex input))))
573 (t (complete-with-action
574 action (ring-elements ring) input predicate)))))))
576 ;;;###autoload
577 (defun capf-autosuggest-setup-comint ()
578 "Setup capf-autosuggest for history suggestion in comint."
579 (capf-autosuggest-mode)
580 (add-hook 'capf-autosuggest-capf-functions #'capf-autosuggest-comint-capf nil t))
582 ;;;###autoload
583 (defun capf-autosuggest-setup-eshell ()
584 "Setup capf-autosuggest for history suggestion in eshell."
585 (capf-autosuggest-mode)
586 (add-hook 'capf-autosuggest-capf-functions #'capf-autosuggest-eshell-capf nil t))
588 ;;;###autoload
589 (defun capf-autosuggest-setup-minibuffer ()
590 "Setup capf-autosuggest for history suggestion in the minibuffer."
591 (let ((hist minibuffer-history-variable)
592 (should-prin1 nil))
593 (when (and (not (eq t hist))
594 (setq hist (symbol-value hist)))
595 (when (eq minibuffer-history-sexp-flag (minibuffer-depth))
596 (setq should-prin1 t))
597 (capf-autosuggest-mode)
598 (add-hook 'capf-autosuggest-capf-functions
599 (lambda ()
600 (when should-prin1
601 (setq hist (mapcar #'prin1-to-string hist)
602 should-prin1 nil))
603 (list (minibuffer-prompt-end)
604 (point-max)
605 hist
606 :exclusive 'no))
607 nil t))))
609 (provide 'capf-autosuggest)
610 ;;; capf-autosuggest.el ends here