unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Juri Linkov <juri@linkov.net>
To: emacs-devel@gnu.org
Subject: zsh-like zcomplete-mode based on icomplete-mode
Date: Sat, 09 Apr 2022 21:45:14 +0300	[thread overview]
Message-ID: <86sfqm14g6.fsf@mail.linkov.net> (raw)

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

Here is the first version of the mode with the behavior like in zsh,
but that is based on icomplete-mode.  So many customizable variables
and commands were copied from icomplete-mode, but instead of displaying
completions as an overlay in the minibuffer, in zcomplete-mode
completions are displayed in the standard *Completions* window.
All other icomplete features are preserved in zcomplete, such as
typing in the minibuffer continuously updates a list of possible
completions that match the typed string:


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

diff --git a/lisp/icomplete.el b/lisp/icomplete.el
index 2986aa192c..1e6b11bbd1 100644
--- a/lisp/icomplete.el
+++ b/lisp/icomplete.el
@@ -1063,6 +1063,173 @@ icomplete-completions
 ;;;###autoload  (make-obsolete 'iswitchb-mode
 ;;;###autoload    "use `icomplete-mode' or `ido-mode' instead." "24.4"))
 
+(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'.")
+
+(defun zcomplete-post-command-hook (_ _ _)
+  (let ((non-essential t)) ;E.g. don't prompt for password!
+    (zcomplete-exhibit)))
+
+(defvar-keymap zcomplete-minibuffer-map
+  :doc "Keymap used by `zcomplete-mode' in the minibuffer."
+  "<remap> <minibuffer-complete-and-exit>" #'zcomplete-ret
+  "<left>"  #'minibuffer-previous-completion
+  "<right>" #'minibuffer-next-completion
+  "<up>"    #'zcomplete-previous-line-completion
+  "<down>"  #'zcomplete-next-line-completion)
+
+(defun zcomplete-ret ()
+  "Exit minibuffer for zcomplete."
+  (interactive)
+  (remove-hook 'after-change-functions #'zcomplete-post-command-hook t)
+  (if (get-buffer-window "*Completions*" 0)
+      (minibuffer-choose-completion)
+    (minibuffer-complete-and-exit)))
+
+(defun zcomplete-previous-line-completion (&optional n)
+  "Run `previous-line' from the minibuffer in its completions window."
+  (interactive "p")
+  (with-minibuffer-completions-window
+    (when completions-highlight-face
+      (setq-local cursor-face-highlight-nonselected-window t))
+    (previous-line n)))
+
+(defun zcomplete-next-line-completion (&optional n)
+  "Run `next-line' from the minibuffer in its completions window."
+  (interactive "p")
+  (with-minibuffer-completions-window
+    (when completions-highlight-face
+      (setq-local cursor-face-highlight-nonselected-window t))
+    (next-line n)))
+
+\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)))
+
+(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))
+    (use-local-map (make-composed-keymap zcomplete-minibuffer-map
+    					 (current-local-map)))
+    (add-hook 'after-change-functions #'zcomplete-post-command-hook nil t)
+    (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)))
+               (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
+        (minibuffer-completion-help)
+        (minibuffer-next-completion)))))
+
 (provide 'icomplete)
 
 ;;;_* Local emacs vars.

             reply	other threads:[~2022-04-09 18:45 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-09 18:45 Juri Linkov [this message]
2022-04-09 20:30 ` zsh-like zcomplete-mode based on icomplete-mode Tassilo Horn
2022-04-10  7:38   ` Juri Linkov
2022-04-10  9:12     ` Tassilo Horn
2022-04-10 16:31       ` [External] : " Drew Adams
2022-04-10 19:11       ` Juri Linkov
2022-04-10  1:05 ` Ergus
2022-04-10  7:38   ` Juri Linkov
2022-04-10 10:41     ` Ergus
2022-04-10 16:34       ` [External] : " Drew Adams
2022-04-10 19:16       ` Juri Linkov
2022-04-10 21:45         ` Ergus
2022-04-11 16:53           ` Juri Linkov
2022-04-12 17:15       ` Juri Linkov

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=86sfqm14g6.fsf@mail.linkov.net \
    --to=juri@linkov.net \
    --cc=emacs-devel@gnu.org \
    /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).