unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Juri Linkov <juri@linkov.net>
To: sbaugh@catern.com
Cc: emacs-devel@gnu.org
Subject: Re: Updating *Completions* as you type
Date: Fri, 13 Oct 2023 09:34:28 +0300	[thread overview]
Message-ID: <86cyxjyr1y.fsf@mail.linkov.net> (raw)
In-Reply-To: <87bkd3z9bi.fsf@catern.com> (sbaugh@catern.com's message of "Thu,  12 Oct 2023 19:53:53 -0400")

[-- Attachment #1: Type: text/plain, Size: 1389 bytes --]

> It would be nice if there was a built-in customization which caused
> *Completions* to update as you type, as long as that buffer is visible.
> I imagine such a request has been made before, so what is the obstacle
> to adding it?

I don't remember what was the obstacle, but here is the previous patch
that implements the behavior of zsh and is based on icomplete-mode.

> I would like to figure out a solution which everyone is happy with, and
> then I would be happy to implement it.  It seems to me that it's just a
> matter of, after each keystroke, triggering (for minibuffer completion)
> minibuffer-completion-help or (for buffer completion) some new function
> to populate *Completions* with all-completions output.

There is one difference between icomplete-mode and zcomplete-mode:
the former uses completion-all-sorted-completions where the
recently used items are at the top of the completion list.
Whereas the latter uses the same alphabetical sorting as TAB
shows in the *Completions* buffer.  Maybe a new function
should allow any sorting order?

You are welcome to implement such a new function
to populate *Completions* with all-completions output.
Currently I have no idea how to do this.

> This could (I guess) be done with after-change-functions, although
> maybe others have a better idea.

icomplete-mode uses post-command-hook, so zcomplete-mode does the same.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: zcomplete.patch --]
[-- Type: text/x-diff, Size: 13430 bytes --]

diff --git a/lisp/zcomplete.el b/lisp/zcomplete.el
new file mode 100644
index 00000000000..75a40c0afd3
--- /dev/null
+++ b/lisp/zcomplete.el
@@ -0,0 +1,317 @@
+;;; zcomplete.el --- zsh-like minibuffer completion based on icomplete -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author: Juri Linkov <juri@linkov.net>
+;; Keywords: completion
+;; Maintainer: emacs-devel@gnu.org
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Like `icomplete' but provides feedback in the *Completions* window
+;; instead of the minibuffer.
+
+;;; Code:
+
+(defgroup zcomplete nil
+  "Show completions dynamically in *Completions* window from minibuffer."
+  :prefix "zcomplete-"
+  :link '(info-link "(emacs)Zcomplete")
+  :group 'minibuffer)
+
+(defcustom zcomplete-show-matches-on-no-input nil
+  "When non-nil, show completions when first prompting for input.
+This means to show completions even when the current minibuffer contents
+is the same as was the initial input after minibuffer activation.
+This also means that if you traverse the list of completions with
+commands and just hit RET without typing any characters,
+the match under point will be chosen instead of the default."
+  :type 'boolean)
+
+(defcustom zcomplete-with-completion-tables t
+  "Specialized completion tables with which zcomplete should operate.
+If this is t, zcomplete operates on all tables.
+Otherwise this should be a list of the completion tables (e.g.,
+`internal-complete-buffer') on which zcomplete should operate."
+  :type '(choice (const :tag "All" t)
+                 (repeat function)))
+
+(defcustom zcomplete-compute-delay .15
+  "Completions-computation stall, used only with large-number completions.
+See `zcomplete-delay-completions-threshold'."
+  :type 'number)
+
+(defcustom zcomplete-delay-completions-threshold 400
+  "Pending-completions number over which to apply `zcomplete-compute-delay'."
+  :type 'integer)
+
+(defcustom zcomplete-max-delay-chars 2
+  "Maximum number of initial chars to apply `zcomplete-compute-delay'."
+  :type 'integer)
+
+(defcustom zcomplete-minibuffer-setup-hook nil
+  "Zcomplete-specific customization of minibuffer setup.
+This hook is run during minibuffer setup if Zcomplete is active."
+  :type 'hook)
+
+\f
+(defvar zcomplete--initial-input nil
+  "Initial input in the minibuffer when `zcomplete-mode' was activated.
+Used to implement the option `zcomplete-show-matches-on-no-input'.")
+
+(defvar zcomplete--previous-input nil
+  "Previous input in the minibuffer before editing it.
+Used to optimize `zcomplete-exhibit' to not be fired while
+moving point in the minibuffer.")
+
+(defun zcomplete-post-command-hook ()
+  (let ((non-essential t)) ;E.g. don't prompt for password!
+    (unless (memq this-command '( previous-history-element next-history-element
+                                  previous-line-or-history-element
+                                  next-line-or-history-element))
+      (zcomplete-exhibit))))
+
+(defcustom zcomplete-auto-exhibit 'visible
+  "Non-nil means to use pop up completions on minibuffer edit."
+  :type '(choice (const :tag "Don't auto pop up completions" nil)
+                 (const :tag "Pop up completions window" t)
+                 (const :tag "Update completions window only when visible"
+                        visible)))
+
+(defcustom zcomplete-arrows 'visible
+  "Non-nil means to use arrows to browse completions from the minibuffer."
+  :type '(choice (const :tag "Don't use arrows" nil)
+                 (const :tag "Use arrows" t)
+                 (const :tag "Use arrows when completions window is visible"
+                        visible)))
+
+(defun zcomplete-visible ()
+  (get-buffer-window "*Completions*" 0))
+
+(defun zcomplete-bind-arrows (binding &optional horizontal)
+  `(menu-item
+    "" ,binding
+    :filter ,(lambda (cmd)
+               (when (or (eq zcomplete-arrows t)
+                         (and (eq zcomplete-arrows 'visible)
+                              (zcomplete-visible)
+                              (or (not horizontal)
+                                  (eq completions-format 'one-column))))
+                 cmd))))
+
+(defun zcomplete-bind-visible (binding)
+  `(menu-item
+    "" ,binding
+    :filter ,(lambda (cmd)
+               (when (zcomplete-visible)
+                 cmd))))
+
+(defvar-keymap zcomplete-minibuffer-mode-map ;; zcomplete-minibuffer-map
+  :doc "Keymap used by `zcomplete-mode' in the minibuffer."
+
+  "<remap> <minibuffer-complete-and-exit>" (zcomplete-bind-visible #'zcomplete-ret)
+  "<remap> <minibuffer-keyboard-quit>"     (zcomplete-bind-visible #'zcomplete-quit)
+  "<remap> <abort-minibuffers>"            (zcomplete-bind-visible #'zcomplete-quit)
+
+  "<left>"    (zcomplete-bind-arrows #'zcomplete-previous-completion t)
+  "<right>"   (zcomplete-bind-arrows #'zcomplete-next-completion t)
+  "<up>"      (zcomplete-bind-arrows #'zcomplete-previous-line-completion)
+  "<down>"    (zcomplete-bind-arrows #'zcomplete-next-line-completion)
+  "<home>"    (zcomplete-bind-arrows #'zcomplete-first-completion)
+  "<end>"     (zcomplete-bind-arrows #'zcomplete-last-completion)
+  "<next>"    (zcomplete-bind-arrows #'scroll-other-window)
+  "<prior>"   (zcomplete-bind-arrows #'scroll-other-window-down))
+
+(defun zcomplete-ret (&optional no-exit no-quit)
+  "Choose the completion from the minibuffer in its completions window."
+  (interactive "P")
+  (condition-case nil
+      (minibuffer-choose-completion no-exit no-quit)
+    (error (minibuffer-complete-and-exit))))
+
+(defun zcomplete-quit ()
+  "Exit minibuffer for zcomplete."
+  (interactive)
+  (minibuffer-hide-completions))
+
+(defcustom zcomplete-auto-choose nil
+  "Non-nil means to automatically insert completions to the minibuffer.
+It affects the variable `minibuffer-completion-auto-choose'.
+This variable is usable only when `zcomplete-auto-exhibit' is nil."
+  :type 'boolean)
+
+(defun zcomplete-next-completion (&optional n)
+  "Run `minibuffer-next-completion' without auto choosing."
+  (interactive "p")
+  (let ((minibuffer-completion-auto-choose zcomplete-auto-choose))
+    (minibuffer-next-completion n)))
+
+(defun zcomplete-previous-completion (&optional n)
+  "Run `minibuffer-previous-completion' without auto choosing."
+  (interactive "p")
+  (let ((minibuffer-completion-auto-choose zcomplete-auto-choose))
+    (minibuffer-previous-completion n)))
+
+(defun zcomplete-next-line-completion (&optional n)
+  "Run `minibuffer-next-line-completion' without auto choosing."
+  (interactive "p")
+  (let ((minibuffer-completion-auto-choose zcomplete-auto-choose))
+    (minibuffer-next-line-completion n)))
+
+(defun zcomplete-previous-line-completion (&optional n)
+  "Run `minibuffer-previous-line-completion' without auto choosing."
+  (interactive "p")
+  (let ((minibuffer-completion-auto-choose zcomplete-auto-choose))
+    (minibuffer-previous-line-completion n)))
+
+(defun zcomplete-first-completion ()
+  "Run `first-completion' from the minibuffer in its completions window."
+  (interactive)
+  (with-minibuffer-completions-window
+    (when completions-highlight-face
+      (setq-local cursor-face-highlight-nonselected-window t))
+    (let ((minibuffer-completion-auto-choose zcomplete-auto-choose))
+      (first-completion))))
+
+(defun zcomplete-last-completion ()
+  "Run `last-completion' from the minibuffer in its completions window."
+  (interactive)
+  (with-minibuffer-completions-window
+    (when completions-highlight-face
+      (setq-local cursor-face-highlight-nonselected-window t))
+    (let ((minibuffer-completion-auto-choose zcomplete-auto-choose))
+      (last-completion))))
+
+\f
+;;;###autoload
+(define-minor-mode zcomplete-mode
+  "Toggle incremental minibuffer completion (Zcomplete mode).
+
+When this global minor mode is enabled, typing in the minibuffer
+continuously displays a list of possible completions that match
+the string you have typed.  The list of completions is displayed
+in the *Completions* window.
+
+For more information, see Info node `(emacs)Zcomplete'.
+For options you can set, `\\[customize-group] zcomplete'.
+
+You can use the following key bindings to navigate and select
+completions:
+
+\\{zcomplete-minibuffer-map}"
+  :global t :group 'zcomplete
+  (remove-hook 'minibuffer-setup-hook #'zcomplete-minibuffer-setup)
+  (when zcomplete-mode
+    (add-hook 'minibuffer-setup-hook #'zcomplete-minibuffer-setup)
+    ;; (setq-default completion-show-help nil
+    ;;               completions-header-format nil)
+    ))
+
+(define-minor-mode zcomplete-minibuffer-mode
+  "Enable arrows in the minibuffer.
+The only purpose of this mode is to activate
+`zcomplete-minibuffer-mode-map' in the minibuffer."
+  :global nil)
+
+(defun zcomplete--completion-table ()
+  (if (window-minibuffer-p) minibuffer-completion-table
+    (or (nth 2 completion-in-region--data)
+        (message "In %S (w=%S): %S"
+                 (current-buffer) (selected-window) (window-minibuffer-p)))))
+(defun zcomplete--field-string ()
+  (if (window-minibuffer-p)
+      (minibuffer-contents)
+    (buffer-substring-no-properties
+     (nth 0 completion-in-region--data)
+     (nth 1 completion-in-region--data))))
+(defun zcomplete--field-beg ()
+  (if (window-minibuffer-p) (minibuffer-prompt-end)
+    (nth 0 completion-in-region--data)))
+(defun zcomplete--field-end ()
+  (if (window-minibuffer-p) (point-max)
+    (nth 1 completion-in-region--data)))
+
+(defun zcomplete-simple-completing-p ()
+  "Non-nil if current window is a minibuffer that's doing simple completion."
+  (unless executing-kbd-macro
+    (let ((table (zcomplete--completion-table)))
+      (and table
+           (or (not (functionp table))
+               (eq zcomplete-with-completion-tables t)
+               (member table zcomplete-with-completion-tables))))))
+
+(defun zcomplete-minibuffer-setup ()
+  "Run in minibuffer on activation to establish incremental completion.
+Usually run by inclusion in `minibuffer-setup-hook'."
+  (when zcomplete-mode
+    (setq-local zcomplete--initial-input (zcomplete--field-string))
+    (setq-local zcomplete--previous-input nil)
+    (zcomplete-minibuffer-mode 1)
+    (add-hook 'post-command-hook #'zcomplete-post-command-hook nil t)
+    (zcomplete-exhibit)
+    (run-hooks 'zcomplete-minibuffer-setup-hook)))
+
+(defun zcomplete-exhibit ()
+  "Update zcomplete completions display.
+Should be run via minibuffer `post-command-hook'.
+See `zcomplete-mode' and `minibuffer-setup-hook'."
+  (when (and zcomplete-mode
+             (zcomplete-simple-completing-p)) ;Shouldn't be necessary.
+    (when (and (or zcomplete-show-matches-on-no-input
+                   (not (equal (zcomplete--field-string)
+                               zcomplete--initial-input)))
+               (not (equal (zcomplete--field-string)
+                           zcomplete--previous-input))
+               (or (eq zcomplete-auto-exhibit t)
+                   (and (eq zcomplete-auto-exhibit 'visible)
+                        (zcomplete-visible)))
+               (or
+                ;; Don't bother with delay after certain number of chars:
+                (> (- (point) (zcomplete--field-beg))
+                   zcomplete-max-delay-chars)
+                ;; Don't delay if the completions are known.
+                completion-all-sorted-completions
+                ;; Don't delay if alternatives number is small enough:
+                (and (sequencep (zcomplete--completion-table))
+                     (< (length (zcomplete--completion-table))
+                        zcomplete-delay-completions-threshold))
+                ;; Delay - give some grace time for next keystroke, before
+                ;; embarking on computing completions:
+                (sit-for zcomplete-compute-delay)))
+      (save-excursion
+        (goto-char (point-max))
+        (setq-local zcomplete--previous-input (zcomplete--field-string))
+        (minibuffer-completion-help)
+        (unless zcomplete-auto-exhibit
+          (zcomplete-first-completion))))))
+
+(let ((keymap completion-in-region-mode-map))
+  (keymap-set keymap "<left>"  #'zcomplete-previous-completion)
+  (keymap-set keymap "<right>" #'zcomplete-next-completion)
+  (keymap-set keymap "<up>"    #'zcomplete-previous-line-completion)
+  (keymap-set keymap "<down>"  #'zcomplete-next-line-completion)
+  (keymap-set keymap "<home>"  #'zcomplete-first-completion)
+  (keymap-set keymap "<end>"   #'zcomplete-last-completion)
+  (keymap-set keymap "<next>"  #'scroll-other-window)
+  (keymap-set keymap "<prior>" #'scroll-other-window-down)
+  (keymap-set keymap "RET"     #'zcomplete-ret))
+
+\f
+(provide 'zcomplete)
+
+;;; zcomplete.el ends here

  parent reply	other threads:[~2023-10-13  6:34 UTC|newest]

Thread overview: 107+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-12 23:53 Updating *Completions* as you type sbaugh
2023-10-13  6:31 ` Eli Zaretskii
2023-10-13 18:01   ` Spencer Baugh
2023-10-14  7:09     ` Eli Zaretskii
2023-10-14 19:26       ` Björn Bidar
     [not found]       ` <874jit2ef7.fsf@>
2023-10-14 19:38         ` Eli Zaretskii
2023-10-14 16:51     ` Juri Linkov
2023-10-14 17:56       ` sbaugh
2023-10-14 19:51       ` Dmitry Gutov
2023-10-13  6:34 ` Juri Linkov [this message]
2023-10-13 19:04   ` Spencer Baugh
2023-10-14 16:58     ` Juri Linkov
2023-10-14 20:05       ` sbaugh
2023-10-15  6:06         ` Eli Zaretskii
2023-10-15 15:55           ` sbaugh
2023-10-16 11:38             ` Eli Zaretskii
2023-10-16 14:50               ` Michael Albinus
2023-10-16 15:58                 ` [External] : " Drew Adams
2023-10-16 12:16             ` sbaugh
2023-10-17 18:23               ` Juri Linkov
2023-10-18 23:27                 ` Spencer Baugh
2023-10-15  7:32         ` Juri Linkov
2023-10-16 19:28           ` Rudolf Adamkovič
2023-10-17 18:38             ` Juri Linkov
2023-10-15 20:31         ` Eshel Yaron
2023-10-16  3:18           ` [External] : " Drew Adams
2023-10-16 16:54           ` Juri Linkov
2023-10-17 13:48         ` sbaugh
2023-10-17 18:35           ` Juri Linkov
2023-10-17 22:57             ` Spencer Baugh
2023-10-18  3:04               ` [External] : " Drew Adams
2023-10-18  6:56               ` Juri Linkov
2023-10-18 12:25                 ` Spencer Baugh
2023-10-18 17:32                   ` Juri Linkov
2023-10-18 23:33                     ` Spencer Baugh
2023-10-19  2:29                       ` Spencer Baugh
2023-10-19  6:55                         ` Juri Linkov
2023-11-19 19:22                           ` sbaugh
2023-11-20  7:51                             ` Juri Linkov
2023-11-20 15:24                               ` Spencer Baugh
2023-11-20 17:47                                 ` Juri Linkov
2023-11-20 18:50                                   ` Spencer Baugh
2023-11-21  7:58                                     ` Juri Linkov
2023-11-21 12:40                                       ` sbaugh
2023-11-21 17:09                                         ` Juri Linkov
2023-11-21 20:45                                           ` Spencer Baugh
2023-11-22  7:51                                             ` Juri Linkov
2023-11-22 16:11                                               ` Spencer Baugh
2023-11-23  7:58                                                 ` Juri Linkov
2023-11-23 12:36                                                   ` sbaugh
2023-11-24  7:58                                                     ` Juri Linkov
2023-11-25 16:44                                                       ` Spencer Baugh
2023-11-25 18:31                                                         ` Juri Linkov
2023-11-26 13:33                                                           ` sbaugh
2023-11-27  7:28                                                             ` Juri Linkov
2023-11-28 14:38                                                               ` Spencer Baugh
2023-11-28 15:03                                                                 ` Eli Zaretskii
2023-11-28 17:13                                                                   ` Juri Linkov
2023-11-28 17:36                                                                     ` Eli Zaretskii
2023-11-29  7:11                                                                       ` Juri Linkov
2023-11-29 13:09                                                                         ` Eli Zaretskii
2023-11-29 14:14                                                                           ` Spencer Baugh
2023-11-29 14:54                                                                             ` Eli Zaretskii
2023-11-29 15:21                                                                               ` Spencer Baugh
2023-11-29 15:52                                                                                 ` Eli Zaretskii
2023-11-29 19:17                                                                                   ` Spencer Baugh
2023-11-30  6:12                                                                                     ` Eli Zaretskii
2023-11-30 12:33                                                                                       ` Spencer Baugh
2023-11-30 14:10                                                                                         ` Eli Zaretskii
2023-11-28 23:56                                                                   ` Spencer Baugh
2023-11-29  3:33                                                                     ` Eli Zaretskii
2023-12-03 17:25                                                                     ` Juri Linkov
2023-12-03 17:56                                                                       ` Eli Zaretskii
2023-12-06 17:17                                                                         ` Juri Linkov
2023-11-28 17:16                                                                 ` Juri Linkov
2023-11-28 23:36                                                                   ` Turning completion table lambdas into symbols Spencer Baugh
2023-11-28 23:51                                                                     ` Dmitry Gutov
2023-11-29 19:26                                                                       ` Spencer Baugh
2023-12-01  0:36                                                                         ` Dmitry Gutov
2023-11-29  7:18                                                                     ` Juri Linkov
2023-11-21 12:54                                       ` Updating *Completions* as you type John Yates
2023-11-21 17:03                                         ` Juri Linkov
2023-11-21 22:27                                           ` John Yates
2023-10-20  6:49         ` Juri Linkov
2023-10-17 15:01       ` sbaugh
2023-10-17 18:20         ` Juri Linkov
2023-10-17 23:37           ` Spencer Baugh
2023-10-17 23:44             ` Spencer Baugh
2023-10-18  6:51             ` Juri Linkov
2023-10-18 12:47               ` Spencer Baugh
2023-10-18 17:28                 ` Juri Linkov
2023-10-18 23:32                   ` Spencer Baugh
2023-10-16  3:19   ` [External] : " Drew Adams
2023-10-20  9:35   ` zcomplete Philip Kaludercic
2023-10-22 17:28     ` zcomplete Juri Linkov
2023-10-23  5:00       ` zcomplete Protesilaos Stavrou
2023-10-23  6:45         ` zcomplete Juri Linkov
2023-10-13 18:11 ` Updating *Completions* as you type Daniel Semyonov
2023-10-13 18:48   ` Spencer Baugh
2023-10-16  3:16     ` [External] : " Drew Adams
2023-10-16  9:25       ` Philip Kaludercic
2023-10-16 16:03         ` Drew Adams
2023-10-20  7:45           ` Philip Kaludercic
2023-10-20 16:10             ` Drew Adams
2023-10-16 22:55         ` Emanuel Berg
2023-10-17  6:09           ` Emanuel Berg
2023-10-17  0:44 ` Michael Heerdegen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86cyxjyr1y.fsf@mail.linkov.net \
    --to=juri@linkov.net \
    --cc=emacs-devel@gnu.org \
    --cc=sbaugh@catern.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).