emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 15ac51e0fd: Merge branch 'feature/completions-customs'


From: Juri Linkov
Subject: master 15ac51e0fd: Merge branch 'feature/completions-customs'
Date: Tue, 22 Mar 2022 04:23:42 -0400 (EDT)

branch: master
commit 15ac51e0fd385130a0764481290d04a71aad5e88
Merge: 966ec5dae6 eba9c473a8
Author: Juri Linkov <juri@linkov.net>
Commit: Juri Linkov <juri@linkov.net>

    Merge branch 'feature/completions-customs'
---
 doc/emacs/mini.texi   | 37 +++++++++++++++++++--
 doc/lispref/text.texi | 11 +++++++
 etc/NEWS              | 32 ++++++++++++++++++
 lisp/minibuffer.el    | 84 +++++++++++++++++++++++++++++++++++------------
 lisp/simple.el        | 90 +++++++++++++++++++++++++++++++++++++++------------
 5 files changed, 212 insertions(+), 42 deletions(-)

diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi
index 13d9269c68..24517262fa 100644
--- a/doc/emacs/mini.texi
+++ b/doc/emacs/mini.texi
@@ -624,11 +624,18 @@ completion alternatives in the completion list.
 @vindex completion-auto-help
   If @code{completion-auto-help} is set to @code{nil}, the completion
 commands never display the completion list buffer; you must type
-@kbd{?}  to display the list.  If the value is @code{lazy}, Emacs only
+@kbd{?} to display the list.  If the value is @code{lazy}, Emacs only
 shows the completion list buffer on the second attempt to complete.
 In other words, if there is nothing to complete, the first @key{TAB}
 echoes @samp{Next char not unique}; the second @key{TAB} shows the
-completion list buffer.
+completion list buffer.  With the previous values and the default
+@code{t} the completions are hidden when some unique completion is
+executed.  If @code{completion-auto-help} is set to @code{always}, the
+completion commands are always shown after a completion attempt, or
+updated if they are already visible.  If the value is @code{visible},
+then completions are not hidden, but updated if they are already
+visible while the current behavior stays the same as default if they
+are not.
 
 @vindex completion-cycle-threshold
   If @code{completion-cycle-threshold} is non-@code{nil}, completion
@@ -651,6 +658,32 @@ changed by changing the @code{completions-format} user 
option.  If
 @code{vertical}, sort the completions vertically in columns instead,
 and if @code{one-column}, just use a single column.
 
+@vindex completions-max-height
+  When @code{completions-max-height} is non-@code{nil}, it limits the
+size of the completions window.  It is specified in lines and include
+mode, header line and a bottom divider, if any.  For a more complex
+control of the Completion window display properties, you can use
+@code{display-buffer-alist} (@pxref{Buffer Display Action
+Alists,,Action Alists for Buffer Display, elisp, The Emacs Lisp
+Reference Manual}).
+
+@vindex completions-header-format
+The variable @code{completions-header-format} is a formatted string to
+control the informative line shown before the completions list of
+candidates.  It may contain a @code{%s} to show the total number of
+completions.  When it is @code{nil}, the message is totally suppressed.
+Text properties may be added to change the appearance, some useful
+ones are @code{face} or @code{cursor-intangible} (@pxref{Special
+Properties,,Properties with Special Meanings, elisp, The Emacs Lisp
+Reference Manual}).
+
+@vindex completions-highlight-face
+When @code{completions-highlight-face} is a face name, then the
+current completion candidate will be highlighted with that face.  The
+default value is @code{completions-highlight}.  When the value is
+@code{nil}, no highlighting is performed.  This feature sets the text
+property @code{cursor-face}.
+
 @node Minibuffer History
 @section Minibuffer History
 @cindex minibuffer history
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 7897adeb05..edb75b453c 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -3551,6 +3551,17 @@ that alter the text size (e.g., @code{:height}, 
@code{:weight}, and
 @code{:slant}).  Those attributes are always the same as for the
 unhighlighted text.
 
+@item cursor-face
+@kindex cursor-face @r{(text property)}
+This property is similar to @code{mouse-face}, but the face is used if
+the cursor (instead of mouse) is on or near the character.  Near has
+the same meaning that in @code{mouse-face} and the highlighting only
+takes effect if the mode @code{cursor-face-highlight-mode} is enabled;
+otherwise no highlighting is performed.  When the variable
+@code{cursor-face-highlight-nonselected-window} is non-@code{nil}, the
+text is highlighted even if the window is not selected that is similar
+to @code{highlight-nonselected-windows} for the region.
+
 @item fontified
 @kindex fontified @r{(text property)}
 This property says whether the text is ready for display.  If
diff --git a/etc/NEWS b/etc/NEWS
index 1ae231afdb..ebf1346dae 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -577,6 +577,31 @@ This option controls the sorting of the completion 
candidates in
 the "*Completions*" buffer.  Available styles are no sorting,
 alphabetical (the default), or a custom sort function.
 
++++
+*** New values for the 'completion-auto-help' option.
+There are two new values to control the way "*Completions*" behave after
+a <tab> if completion is not unique.  'always' updates or shows
+the "*Completions*" buffer after any attempt to complete.  'visual' is
+like 'always', but only update the completions if they are already
+visible.  The default value 't' always hides the completion buffer after
+some completion is made.
+
++++
+*** New user option 'completions-max-height'.
+This option limits the height of the "*Completions*" buffer.
+
++++
+*** New option 'completions-header-format'
+This is a string to control the message to show before completions.
+It may contain a "%s" to show the total number of completions. If nil no
+completions are shown.
+
++++
+*** New option 'completions-highlight-face'.
+When this variable is a face name, it highlights the current candidate
+in the "*Completions*" buffer with that face.  When the value is nil,
+no highlighting is performed at all.
+
 ** Isearch and Replace
 
 +++
@@ -1301,6 +1326,13 @@ property.
 ** New 'min-width' 'display' property.
 This allows setting a minimum display width for a region of text.
 
++++
+** New 'cursor-face' text property.
+This uses 'cursor-face' instead of the default face when cursor is on or
+near the character and 'cursor-face-highlight-mode' is enabled.  The
+variable 'cursor-face-highlight-nonselected-window' is similar to
+'highlight-nonselected-windows', but for this property.
+
 +++
 ** New event type 'touch-end'.
 This event is sent whenever the user's finger moves off the mouse
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 36b8d80841..00d4560865 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -897,8 +897,17 @@ If the current buffer is not a minibuffer, erase its 
entire contents."
 If the value is t the *Completions* buffer is displayed whenever completion
 is requested but cannot be done.
 If the value is `lazy', the *Completions* buffer is only displayed after
-the second failed attempt to complete."
-  :type '(choice (const nil) (const t) (const lazy)))
+the second failed attempt to complete.
+If the value is 'always', the completion commands are always shown
+after a completion attempt, or updated if they are already visible.
+If the value is 'visible', then completions are not hidden, but updated
+if they are already visible while the current behavior stays the same
+as default if they are not."
+  :type '(choice (const :tag "Disabled" nil)
+                 (const :tag "Enabled legacy" t)
+                 (const :tag "After a second attempt" lazy)
+                 (const :tag "Visible update" visible)
+                 (const :tag "Always update" always)))
 
 (defvar completion-styles-alist
   '((emacs21
@@ -1343,16 +1352,18 @@ when the buffer's text is already an exact match."
               (completion--cache-all-sorted-completions beg end comps)
               (minibuffer-force-complete beg end))
              (completed
-              ;; We could also decide to refresh the completions,
-              ;; if they're displayed (and assuming there are
-              ;; completions left).
-              (minibuffer-hide-completions)
-              (if exact
-                  ;; If completion did not put point at end of field,
-                  ;; it's a sign that completion is not finished.
-                  (completion--done completion
-                                    (if (< comp-pos (length completion))
-                                        'exact 'unknown))))
+              (cond
+               ((pcase completion-auto-help
+                  ('visible (get-buffer-window "*Completions*" 0))
+                  ('always t))
+                (minibuffer-completion-help beg end))
+               (t (minibuffer-hide-completions)
+                  (when exact
+                    ;; If completion did not put point at end of field,
+                    ;; it's a sign that completion is not finished.
+                    (completion--done completion
+                                      (if (< comp-pos (length completion))
+                                          'exact 'unknown))))))
              ;; Show the completion table, if requested.
              ((not exact)
              (if (pcase completion-auto-help
@@ -1842,6 +1853,17 @@ Return nil if there is no valid completion, else t."
 This face is only used if the strings used for completions
 doesn't already specify a face.")
 
+(defface completions-highlight
+  '((t :inherit highlight))
+  "Default face for highlighting the current completion candidate."
+  :version "29.1")
+
+(defcustom completions-highlight-face 'completions-highlight
+  "A face name to highlight the current completion candidate.
+If the value is nil, no highlighting is performed."
+  :type '(choice (const nil) face)
+  :version "29.1")
+
 (defcustom completions-format 'horizontal
   "Define the appearance and sorting of completions.
 If the value is `vertical', display completions sorted vertically
@@ -1861,6 +1883,17 @@ completions."
   :type 'boolean
   :version "28.1")
 
+(defcustom completions-header-format
+  (propertize "%s possible completions:\n"
+              'face 'shadow
+              :help "Please select a completion")
+  "Format of completions header.
+It may contain one %s to show the total count of completions.
+When nil, no header is shown."
+  :type '(choice (const :tag "No header" nil)
+                 (string :tag "Header format string"))
+  :version "29.1")
+
 (defun completion--insert-strings (strings &optional group-fun)
   "Insert a list of STRINGS into the current buffer.
 The candidate strings are inserted into the buffer depending on the
@@ -2012,7 +2045,7 @@ Runs of equal candidate strings are eliminated.  
GROUP-FUN is a
               (funcall group-fun str 'transform)
             str))
          (point))
-       `(mouse-face highlight completion--string ,str))
+       `(mouse-face highlight cursor-face ,completions-highlight-face 
completion--string ,str))
     ;; If `str' is a list that has 2 elements,
     ;; then the second element is a suffix annotation.
     ;; If `str' has 3 elements, then the second element
@@ -2123,10 +2156,9 @@ candidates."
 
     (with-current-buffer standard-output
       (goto-char (point-max))
-      (if (null completions)
-          (insert "There are no possible completions of what you have typed.")
-        (insert "Possible completions are:\n")
-        (completion--insert-strings completions group-fun))))
+      (when completions-header-format
+        (insert (format completions-header-format (length completions))))
+      (completion--insert-strings completions group-fun)))
 
   (run-hooks 'completion-setup-hook)
   nil)
@@ -2198,6 +2230,19 @@ variables.")
                (equal pre-msg (and exit-fun (current-message))))
       (completion--message message))))
 
+(defcustom completions-max-height nil
+  "Maximum height for *Completions* buffer window."
+  :type '(choice (const nil) natnum)
+  :version "29.1")
+
+(defun completions--fit-window-to-buffer (&optional win &rest _)
+  "Resize *Completions* buffer window."
+  (if temp-buffer-resize-mode
+      (let ((temp-buffer-max-height (or completions-max-height
+                                        temp-buffer-max-height)))
+        (resize-temp-buffer-window win))
+    (fit-window-to-buffer win completions-max-height)))
+
 (defun minibuffer-completion-help (&optional start end)
   "Display a list of possible completions of the current minibuffer contents."
   (interactive)
@@ -2261,9 +2306,7 @@ variables.")
              ,(if (eq (selected-window) (minibuffer-window))
                   'display-buffer-at-bottom
                 'display-buffer-below-selected))
-            ,(if temp-buffer-resize-mode
-                 '(window-height . resize-temp-buffer-window)
-               '(window-height . fit-window-to-buffer))
+            (window-height . completions--fit-window-to-buffer)
             ,(when temp-buffer-resize-mode
                '(preserve-size . (nil . t)))
             (body-function
@@ -2354,6 +2397,7 @@ variables.")
   "Get rid of an out-of-date *Completions* buffer."
   ;; FIXME: We could/should use minibuffer-scroll-window here, but it
   ;; can also point to the minibuffer-parent-window, so it's a bit tricky.
+  (interactive)
   (let ((win (get-buffer-window "*Completions*" 0)))
     (if win (with-selected-window win (bury-buffer)))))
 
diff --git a/lisp/simple.el b/lisp/simple.el
index 43a0d1efc1..59c86cf778 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -6483,27 +6483,38 @@ An example is a rectangular region handled as a list of
 separate contiguous regions for each line."
   (cdr (region-bounds)))
 
+(defun redisplay--unhighlight-overlay-function (rol)
+  "If ROL is an overlay, call `delete-overlay'."
+  (when (overlayp rol) (delete-overlay rol)))
+
 (defvar redisplay-unhighlight-region-function
-  (lambda (rol) (when (overlayp rol) (delete-overlay rol))))
+  #'redisplay--unhighlight-overlay-function
+  "Function to remove the region-highlight overlay.")
+
+(defun redisplay--highlight-overlay-function (start end window rol &optional 
face)
+  "Update the overlay ROL in WINDOW with FACE in range START-END."
+  (unless face (setq face 'region))
+  (if (not (overlayp rol))
+      (let ((nrol (make-overlay start end)))
+        (funcall redisplay-unhighlight-region-function rol)
+        (overlay-put nrol 'window window)
+        (overlay-put nrol 'face face)
+        ;; Normal priority so that a large region doesn't hide all the
+        ;; overlays within it, but high secondary priority so that if it
+        ;; ends/starts in the middle of a small overlay, that small overlay
+        ;; won't hide the region's boundaries.
+        (overlay-put nrol 'priority '(nil . 100))
+        nrol)
+    (unless (eq (overlay-get rol 'face) face)
+      (overlay-put rol 'face face))
+    (unless (and (eq (overlay-buffer rol) (current-buffer))
+                 (eq (overlay-start rol) start)
+                 (eq (overlay-end rol) end))
+      (move-overlay rol start end (current-buffer)))
+    rol))
 
 (defvar redisplay-highlight-region-function
-  (lambda (start end window rol)
-    (if (not (overlayp rol))
-        (let ((nrol (make-overlay start end)))
-          (funcall redisplay-unhighlight-region-function rol)
-          (overlay-put nrol 'window window)
-          (overlay-put nrol 'face 'region)
-          ;; Normal priority so that a large region doesn't hide all the
-          ;; overlays within it, but high secondary priority so that if it
-          ;; ends/starts in the middle of a small overlay, that small overlay
-          ;; won't hide the region's boundaries.
-          (overlay-put nrol 'priority '(nil . 100))
-          nrol)
-      (unless (and (eq (overlay-buffer rol) (current-buffer))
-                   (eq (overlay-start rol) start)
-                   (eq (overlay-end rol) end))
-        (move-overlay rol start end (current-buffer)))
-      rol))
+  #'redisplay--highlight-overlay-function
   "Function to move the region-highlight overlay.
 This function is called with four parameters, START, END, WINDOW
 and OVERLAY.  If OVERLAY is nil, a new overlay is created.  In
@@ -6528,8 +6539,33 @@ The overlay is returned by the function.")
               (funcall redisplay-highlight-region-function
                        start end window rol)))
         (unless (equal new rol)
-          (set-window-parameter window 'internal-region-overlay
-                                new))))))
+          (set-window-parameter window 'internal-region-overlay new))))))
+
+(defcustom cursor-face-highlight-nonselected-window nil
+  "Non-nil means highlight text with `cursor-face' even in nonselected windows.
+This variable is similar to `highlight-nonselected-windows'."
+  :local t
+  :type 'boolean
+  :version "29.1")
+
+(defun redisplay--update-cursor-face-highlight (window)
+  "Highlights the overlay used to highlight text with cursor-face."
+  (let ((rol (window-parameter window 'internal-cursor-face-overlay)))
+    (if-let* (((or cursor-face-highlight-nonselected-window
+                   (eq window (selected-window))
+                   (and (window-minibuffer-p)
+                        (eq window (minibuffer-selected-window)))))
+              (pt (window-point window))
+              (cursor-face (get-text-property pt 'cursor-face)))
+        (let* ((start (previous-single-property-change
+                       (1+ pt) 'cursor-face nil (point-min)))
+               (end (next-single-property-change
+                     pt 'cursor-face nil (point-max)))
+               (new (redisplay--highlight-overlay-function
+                     start end window rol cursor-face)))
+          (unless (equal new rol)
+            (set-window-parameter window 'internal-cursor-face-overlay new)))
+      (redisplay--unhighlight-overlay-function rol))))
 
 (defvar pre-redisplay-functions (list #'redisplay--update-region-highlight)
   "Hook run just before redisplay.
@@ -6537,6 +6573,15 @@ It is called in each window that is to be redisplayed.  
It takes one argument,
 which is the window that will be redisplayed.  When run, the `current-buffer'
 is set to the buffer displayed in that window.")
 
+(define-minor-mode cursor-face-highlight-mode
+  "When enabled, respect the cursor-face property."
+  :global nil
+  (if cursor-face-highlight-mode
+      (add-hook 'pre-redisplay-functions
+                #'redisplay--update-cursor-face-highlight nil t)
+    (remove-hook 'pre-redisplay-functions
+                 #'redisplay--update-cursor-face-highlight t)))
+
 (defun redisplay--pre-redisplay-functions (windows)
   (with-demoted-errors "redisplay--pre-redisplay-functions: %S"
     (if (null windows)
@@ -9355,6 +9400,11 @@ Called from `temp-buffer-show-hook'."
       (if base-dir (setq default-directory base-dir))
       (when completion-tab-width
         (setq tab-width completion-tab-width))
+      ;; Maybe enable cursor completions-highlight.
+      (when completions-highlight-face
+        ;; Keep highlighting even if not selected.
+        (setq-local cursor-face-highlight-nonselected-window t)
+        (cursor-face-highlight-mode 1))
       ;; Maybe insert help string.
       (when completion-show-help
        (goto-char (point-min))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]