]> git.eshelyaron.com Git - emacs.git/commitdiff
Change cursor type when showing completion preview
authorEshel Yaron <me@eshelyaron.com>
Sat, 27 Apr 2024 19:08:17 +0000 (21:08 +0200)
committerEshel Yaron <me@eshelyaron.com>
Mon, 29 Apr 2024 14:54:17 +0000 (16:54 +0200)
* lisp/completion-preview.el (completion-preview-cursor-type):
New user option.
(completion-preview--window): New internal variable.
(completion-preview--window-selection-change): Bind it.
(completion-preview--original-cursor-type): New local var.
(completion-preview-active-mode, completion-preview--show): Set
'cursor-type' window parameter according to
'completion-preview-cursor-type', or reset it.
(completion-preview--post-command): Reset cursor type in
'completion-preview--window' if last command switched windows.

lisp/completion-preview.el

index ba81210e36a031fb2c0afa4db2dc089d86949a6a..4e2f013b57a14da40d1d64e9493ac049fed29b14 100644 (file)
 ;; prefix (so nothing is underlined in the preview), it displays a list
 ;; of all matching completion candidates.
 ;;
+;; Completion Preview mode can change the cursor shape while displaying
+;; the preview right after point.  By default, it uses a vertical bar
+;; that clearly separates the completion preview from the preceding
+;; buffer text.  This is especially useful if otherwise your cursor is
+;; otherwise box shaped, since a box cursor on top of the first
+;; character of the completion preview might make it harder to tell
+;; whether that character is part of the preview or not.  To select
+;; another cursor shape, or to disable this feature entirely, customize
+;; the user option `completion-preview-cursor-type'.
+;;
 ;; If you set the user option `completion-preview-exact-match-only' to
 ;; non-nil, Completion Preview mode only suggests a completion
 ;; candidate when its the only possible completion for the (partial)
@@ -220,6 +230,9 @@ Completion Preview mode avoids updating the preview after these commands.")
   "Return property PROP of the completion preview overlay."
   (overlay-get completion-preview--overlay prop))
 
+(defvar completion-preview--window nil
+  "Window showing the completion preview, or nil when there is none.")
+
 (defun completion-preview--window-selection-change (window)
   "Hide completion preview in WINDOW after switching to another window.
 Completion Preview mode adds this function to
@@ -227,16 +240,55 @@ Completion Preview mode adds this function to
   (unless (or (eq window (selected-window))
               (eq window (minibuffer-selected-window)))
     (with-current-buffer (window-buffer window)
-      (completion-preview-active-mode -1))))
+      (let ((completion-preview--window window))
+        (completion-preview-active-mode -1)))))
+
+(defvar-local completion-preview--original-cursor-type 'unset
+  "Value of `cursor-type' to restore after dismissing the completion preview.
+A value of `unset' means there's nothing to restore.")
+
+(defcustom completion-preview-cursor-type 'bar
+  "Type of cursor to use when completion preview is visible.
+If nil, Completion Preview mode does not alter the cursor.  Otherwise,
+the `cursor-type' window parameter of the selected window is temporarily
+set to the value of this option while the completion preview is visible."
+  :type `(choice (const :tag "Leave cursor unchanged" nil)
+                 ,@(remove '(const :tag "None " nil)
+                           (cdr (get 'cursor-type 'custom-type)))
+                 (const :tag "None" none)))
+
+(defsubst completion-preview--cursor-modified-p ()
+  "Return non-nil if the cursor shape is currently modified."
+  (not (eq completion-preview--original-cursor-type 'unset)))
+
+(defsubst completion-preview--restore-cursor ()
+  (set-window-parameter completion-preview--window 'cursor-type
+                        completion-preview--original-cursor-type)
+  (setq completion-preview--original-cursor-type 'unset))
+
+(defsubst completion-preview--modify-cursor ()
+  (setq completion-preview--original-cursor-type
+        (window-parameter completion-preview--window 'cursor-type))
+  (set-window-parameter completion-preview--window
+                        'cursor-type completion-preview-cursor-type))
 
 (define-minor-mode completion-preview-active-mode
   "Mode for when the completion preview is shown."
   :interactive nil
   (if completion-preview-active-mode
-      (add-hook 'window-selection-change-functions
-                #'completion-preview--window-selection-change nil t)
+      (progn
+        (add-hook 'window-selection-change-functions
+                  #'completion-preview--window-selection-change nil t)
+        (when (and completion-preview-cursor-type
+                   (not (completion-preview--cursor-modified-p))
+                   (= (point) (completion-preview--get 'completion-preview-end)))
+          (completion-preview--modify-cursor))
+        (setq completion-preview--window (selected-window)))
     (remove-hook 'window-selection-change-functions
                  #'completion-preview--window-selection-change t)
+    (when (completion-preview--cursor-modified-p)
+      (completion-preview--restore-cursor))
+    (setq completion-preview--window nil)
     (completion-preview-hide)))
 
 (defvar completion-preview-completion-styles '(basic)
@@ -369,6 +421,16 @@ point, otherwise hide it."
            (common (completion-preview--get 'completion-preview-common))
            (suffix (nth index sufs))
            (cand nil))
+      (if (completion-preview--cursor-modified-p)
+          ;; If we have changed the cursor type, check if point is still
+          ;; at the beginning of the preview overlay, and restore the
+          ;; original cursor type if not.
+          (unless (= (point) end) (completion-preview--restore-cursor))
+        ;; Otherwise, if we haven't changed the cursor type, that means
+        ;; point was not at the beginning of the preview overlay.  Maybe
+        ;; now it is, in which case we need to set the cursor type now.
+        (when (and completion-preview-cursor-type (= (point) end))
+          (completion-preview--modify-cursor)))
       (set-text-properties 0 (length suffix)
                            (list 'face (if (cdr sufs)
                                            'completion-preview
@@ -390,6 +452,17 @@ point, otherwise hide it."
 
 (defun completion-preview--post-command ()
   "Create, update or delete completion preview post last command."
+  ;; If the last command switched from one window showing this buffer to
+  ;; another, the buffer-local `post-command-hook' might run before
+  ;; `window-selection-change-functions'.  Prompty restore the cursor in
+  ;; the previously selected window, since we may remove our function
+  ;; from `window-selection-change-functions' before it gets a chance to
+  ;; do so.
+  (unless (or (eq completion-preview--window (selected-window))
+              (eq completion-preview--window (minibuffer-selected-window)))
+    (when (completion-preview--cursor-modified-p)
+      (completion-preview--restore-cursor))
+    (setq completion-preview--window nil))
   (let ((internal-p (or completion-preview--inhibit-update-p
                         (memq this-command
                               completion-preview--internal-commands))))