]> git.eshelyaron.com Git - emacs.git/commitdiff
New command 'crm-complete-and-insert-separator'
authorEshel Yaron <me@eshelyaron.com>
Sat, 20 Jan 2024 08:32:29 +0000 (09:32 +0100)
committerEshel Yaron <me@eshelyaron.com>
Sat, 20 Jan 2024 08:51:27 +0000 (09:51 +0100)
* lisp/emacs-lisp/crm.el (crm-complete-and-insert-separator): New
command.
(completing-read-multiple-mode-map): Bind it to 'C-,'.
(crm-canonical-separator, crm-common-separators): New variables.
(completions-multi-mode): Adapt mode line lighter.
(crm-complete-and-exit): Only suggest in 'M-x' in 'minibuffer-mode'.
(crm-change-separator): (Re)set canonical separator.

* doc/lispref/minibuf.texi (Minibuffer Completion): Update.
* doc/emacs/mini.texi (Completion Multi): New subsection.
(Completion Commands): Fix markup.
(Completion)
* doc/emacs/emacs.texi (Top): Add menu entry.

* etc/NEWS: Announce 'crm-complete-and-insert-separator'.

doc/emacs/emacs.texi
doc/emacs/mini.texi
doc/lispref/minibuf.texi
etc/NEWS
lisp/emacs-lisp/crm.el

index b9be2de49fd5509e2d66606a59cc29b213cefa74..5b0a5b98d80a28ce0a8af21592cd36c280e871e4 100644 (file)
@@ -295,6 +295,7 @@ Completion
 * Completion Exit::     Completion and minibuffer text submission.
 * Completion Styles::   How completion matches are chosen.
 * Narrow Completions::  Restricting completion candidates.
+* Completion Multi::    Providing multiple inputs at once.
 * Completion Options::  Options for completion.
 
 Help
index 84f648dd99930ee20e85b27fc78de949d1766212..64d3e81d50fee8e5626e8024d2dd025120102028 100644 (file)
@@ -293,6 +293,7 @@ Completion}.
 * Completion Exit::          Completion and minibuffer text submission.
 * Completion Styles::        How completion matches are chosen.
 * Narrow Completions::       Restricting completion candidates.
+* Completion Multi::         Providing multiple inputs at once.
 * Completion Options::       Options for completion.
 @end menu
 
@@ -489,8 +490,8 @@ there are.
 
 @kindex C-x / @r{(completion)}
 @findex minibuffer-set-completion-styles
-  @kbd{C-x /} (minibuffer-set-completion-styles) lets you set the
-completion styles for the current minibuffer.  @xref{Completion
+  @kbd{C-x /} (@code{minibuffer-set-completion-styles}) lets you set
+the completion styles for the current minibuffer.  @xref{Completion
 Styles}.  This command prompts you for a list of completion styles,
 and sets that list as the effective completion styles for following
 completion operations in the current minibuffer.  With a plain prefix
@@ -822,6 +823,62 @@ If you invoke this command with a prefix argument (@kbd{C-u C-x n w}),
 it removes all restrictions without prompting, regardless of how many
 there are.
 
+@node Completion Multi
+@subsection Read and Complete Multiple Inputs
+
+@cindex multiple inputs, with completion
+  Some commands read @emph{multiple inputs} from the minibuffer at
+once. For example, @kbd{M-x describe-face} prompts you for @emph{one
+or more} face names, and displays a help buffer describing all given
+faces.  @xref{Faces}.  This works just like reading a single inputs,
+except that you can type several inputs in the minibuffer, separating
+them with @dfn{input separators}.  You can use completion to fill in
+the individual inputs, as usual.
+
+@cindex input separators, for reading multiple inputs
+@cindex separator pattern, for reading multiple inputs
+  The input separator is typically a comma (@samp{,}), so if a command
+@kbd{M-x foo} reads multiple inputs, and you type @kbd{bar,baz,spam
+@key{RET}} in the minibuffer, then @code{foo} gets a list of three
+inputs: @samp{bar}, @samp{baz} and @samp{spam}.  More generally, Emacs
+treats a part of your minibuffer input as an input separator when it
+matches the current @dfn{separator pattern}---a regular expression
+that may change from command to command.  The default separator
+pattern, which most commands use, matches a comma along with any
+surrounding spaces or tabs.  When reading multiple inputs, the
+@file{*Completions*} buffer displays the @samp{Multi} indicator in the
+mode line.  You can hover over that indicator with the mouse to get
+help about the current input separator pattern.
+
+  The following commands are available in the minibuffer while reading
+multiple inputs:
+
+@kindex C-x ,
+@findex crm-change-separator
+  @kbd{C-x ,} (@code{crm-change-separator}) prompts you for a regular
+expression, and sets the current input separator pattern to that
+regular expression.  Use this command if the default separator pattern
+is inconvenient or mistakes a part of your input to be a separator.
+With a prefix argument, that is if you type @kbd{C-u C-x ,}, this
+command also replaces all current input separators in the minibuffer
+with a new separator that you provide.
+
+@kindex C-,
+@findex crm-complete-and-insert-separator
+  @kbd{C-,} (@code{crm-complete-and-insert-separator}) completes
+partial inputs in the minibuffer, and then inserts a new input
+separator at the end of the minibuffer and puts point after it, for
+you to type another input.  In order to insert a separator for you,
+this command must get a hold of a string that matches the current
+separator pattern.  Some commands that read multiple inputs specify a
+so-called @dfn{canonical separator}, in which case @kbd{C-,} uses the
+canonical separator.  Otherwise, this command tries to find an
+appropriate separator by looking at your current input, and applying
+some heuristics that work for common separator patterns.  In case
+@code{C-,} cannot figure out which separator to insert by itself, it
+prompts you for a separator and remembers your choice as the canonical
+separator for the current minibuffer.
+
 @node Completion Options
 @subsection Completion Options
 
index d81f47418ad2e36eeb8273a1cd3e80403a2d5f57..eff81be37cff5f7cec80f37ac42fd5f8de75ac66 100644 (file)
@@ -1270,20 +1270,14 @@ The value of this variable is a regular expression that matches
 @code{completing-read-multiple} input separators.  By default, this is
 set to @samp{[ \t]*,[ \t]*}, which means that a comma, possibly
 surrounded by spaces or tabs, separates
-@code{completing-read-multiple} inputs.
+@code{completing-read-multiple} inputs.  You can also set this
+variable to a cons cell @code{(@var{regexp} . @var{canonical})}, where
+@var{regexp} is the regular expression for matching separators, and
+@var{canonical} is a ``canonical'' separator string that Emacs uses
+when it inserts a separator in behalf of the user.  If @var{canonical}
+does not match @var{regexp}, @var{canonical} is ignored.
 @end defvar
 
-@deffn Command crm-change-separator
-This command, bound to @kbd{C-x ,} in the minibuffer during
-@code{completing-read-multiple}, changes the current input separator.
-It prompts for a new separator regular expression, and sets the local
-value of @code{crm-separator} to that regular expression.  With a
-prefix argument, this command also prompts for a replacement string
-(that should match the new separator) and replaces all of the existing
-separators in the minibuffer with that replacement string.
-@end deffn
-
-
 @node Completion Commands
 @subsection Minibuffer Commands that Do Completion
 
index ffee287b23096e379d3e1ec430ad74ac36d0fbe3..ee02f90bf57a2727a717f53d2f824e4eb61acc82 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -809,6 +809,13 @@ This command lets you change the separator that
 strings.  'completing-read-multiple' binds 'C-x ,' to
 'crm-change-separator' in the minibuffer.
 
++++
+*** New command 'crm-complete-and-insert-separator'.
+This command, bound to 'C-,' in 'completing-read-multiple'
+minibuffers, completes partial inputs that are already in the
+minibuffer, and inserts a new separator at the end of the minibuffer
+for you to insert a another input.
+
 +++
 *** New command 'minibuffer-set-completion-styles'.
 This command, bound to 'C-x /' in the minibuffer, lets you set the
index fe69d0735935e3ca4da72f4c1de6aa4aa2c8bbd9..f9a2a1f049c864a998c9944fcb5b9a8433b1194e 100644 (file)
 
 (defvar crm-separator "[ \t]*,[ \t]*"
   "Separator regexp used for separating strings in `completing-read-multiple'.
-It should be a regexp that does not match the list of completion candidates.")
+It should be a regexp that does not match the list of completion candidates.
+
+This can also be a cons cell (REGEXP . CANONICAL), where REGEXP
+is the separator regexp used for matching input separators, and
+CANONICAL is a canonical separator string that Emacs uses when it
+inserts a separator for you.  If CANONICAL does not match REGEXP,
+it is ignored.  See also `crm-complete-and-insert-separator'.")
+
+(defvar crm-common-separators '(",")
+  "List of strings often used to separate multiple minibuffer inputs.
+
+See also `crm-complete-and-insert-separator'.")
 
 (defvar crm-current-separator nil
   "The value of `crm-separator' for the current minibuffer.")
 
+(defvar crm-canonical-separator nil
+  "Canonical separator for `completing-read-multiple'.
+
+This can either a string that matches `crm-current-separator', or
+nil when there is no canonical separator.")
+
 (defun crm-complete-and-exit ()
   "If all of the minibuffer elements are valid completions then exit.
 All elements in the minibuffer must match.  If there is a mismatch, move point
 to the location of mismatch and do not exit.
 
 This function is modeled after `minibuffer-complete-and-exit'."
-  (interactive)
+  (interactive "" minibuffer-mode)
   (let ((bob (minibuffer--completion-prompt-end))
         (doexit t))
     (goto-char bob)
@@ -177,20 +194,91 @@ for REP as well."
     (goto-char (minibuffer-prompt-end))
     (while (re-search-forward crm-current-separator nil t)
       (replace-match rep t t)))
-  (setq crm-current-separator sep)
+  (setq crm-current-separator sep crm-canonical-separator rep)
   (when (get-buffer-window "*Completions*" 0)
     ;; Update *Completions* to avoid stale `completion-base-affixes'.
     (minibuffer-completion-help)))
 
+(defun crm-complete-and-insert-separator ()
+  "Complete partial inputs and then insert a new input separator.
+
+If `crm-canonical-separator' is non-nil and matches the regular
+expression `crm-current-separator', then this command uses
+`crm-canonical-separator' as the separator.  Otherwise, this
+command tries to find an appropriate separator by matching
+`crm-current-separator' against your current input and against
+the list of common separators in `crm-common-separators', and if
+that fails this command prompts you for the separator to use."
+  (interactive "" minibuffer-mode)
+  (let ((bob (minibuffer--completion-prompt-end))
+        (all-complete t)
+        (enable-recursive-minibuffers t))
+    ;; Establish a canonical separator string, so we can insert it.
+    (setq crm-canonical-separator
+          (or
+           ;; If `crm-canonical-separator' matches, use it.
+           (and (stringp crm-canonical-separator)
+                (string-match-p crm-current-separator
+                                crm-canonical-separator)
+                crm-canonical-separator)
+           ;; If there's some separator already, use that.
+           (and (save-excursion
+                  (goto-char bob)
+                  (re-search-forward crm-current-separator nil t))
+                (buffer-substring-no-properties (match-beginning 0)
+                                                (match-end 0)))
+           ;; If any common separator matches, use it.
+           (seq-some (lambda (sep)
+                       (and (string-match-p crm-current-separator sep)
+                            sep))
+                     crm-common-separators)
+           ;; Ask the user for help.
+           (read-string-matching crm-current-separator
+                                 "Separate inputs with: ")))
+    (while
+        (and all-complete
+             (let* ((beg (save-excursion
+                           (if (re-search-backward crm-current-separator bob t)
+                               (match-end 0)
+                             bob)))
+                    (end (copy-marker
+                          (save-excursion
+                            (if (re-search-forward crm-current-separator nil t)
+                                (match-beginning 0)
+                              (point-max)))
+                          t)))
+               (goto-char end)
+               (setq all-complete nil)
+               (completion-complete-and-exit
+                beg end (lambda () (setq all-complete t)))
+               (goto-char end)
+               (not (eobp)))
+             (looking-at crm-current-separator))
+      (when all-complete
+        (goto-char (match-end 0))))
+    (when all-complete
+      (if (looking-back crm-current-separator bob)
+          ;; Separator already present, show completion candidates.
+          (minibuffer-completion-help)
+        (insert crm-canonical-separator)))))
+
 (define-minor-mode completions-multi-mode
   "Minor mode for reading multiple strings in the minibuffer."
   :lighter (:eval
-            (propertize " Multi" 'help-echo
-                        (concat
-                         "Insert multiple inputs by separating them with \""
-                         (buffer-local-value 'crm-current-separator
-                                             completion-reference-buffer)
-                         "\""))))
+            (let ((canonical
+                   (buffer-local-value 'crm-canonical-separator
+                                       completion-reference-buffer)))
+              (propertize
+               (concat
+                " Multi"
+                (when canonical (concat "[" crm-canonical-separator "]")))
+               'help-echo
+               (concat
+                "Insert multiple inputs by separating them with \""
+                (or canonical
+                    (buffer-local-value 'crm-current-separator
+                                        completion-reference-buffer))
+                "\"")))))
 
 (defun crm-completion-setup ()
   "Enable `completions-multi-mode' in *Completions* buffer."
@@ -205,7 +293,8 @@ for REP as well."
 (defvar-keymap completing-read-multiple-mode-map
   :doc "Keymap for `completing-read-multiple-mode'."
   "<remap> <minibuffer-complete-and-exit>" #'crm-complete-and-exit
-  "C-x ," #'crm-change-separator)
+  "C-x ," #'crm-change-separator
+  "C-," #'crm-complete-and-insert-separator)
 
 (define-minor-mode completing-read-multiple-mode
   "Minor mode for reading multiple strings in the minibuffer."
@@ -235,7 +324,11 @@ contents of the minibuffer are \"alice,bob,eve\" and point is between
 
 This function returns a list of the strings that were read,
 with empty strings removed."
-  (let ((crm-current-separator crm-separator))
+  (let ((crm-current-separator
+         (if (consp crm-separator)
+             (car crm-separator)
+           crm-separator))
+        (crm-canonical-separator (cdr-safe crm-separator)))
     (split-string
      (minibuffer-with-setup-hook
          #'completing-read-multiple-mode