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