]> git.eshelyaron.com Git - emacs.git/commitdiff
Make completion preview cleanup more robust
authorEshel Yaron <me@eshelyaron.com>
Mon, 29 Apr 2024 19:45:25 +0000 (21:45 +0200)
committerEshel Yaron <me@eshelyaron.com>
Mon, 29 Apr 2024 19:45:25 +0000 (21:45 +0200)
lisp/completion-preview.el
test/lisp/completion-preview-tests.el

index 4e2f013b57a14da40d1d64e9493ac049fed29b14..d2926b00c5a1c79b49d71940589ccd087e6df6da 100644 (file)
@@ -230,20 +230,13 @@ 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--buffer nil
+  "Buffer showing the completion preview, or nil when there is none.")
+
 (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
-`window-selection-change-functions', which see."
-  (unless (or (eq window (selected-window))
-              (eq window (minibuffer-selected-window)))
-    (with-current-buffer (window-buffer window)
-      (let ((completion-preview--window window))
-        (completion-preview-active-mode -1)))))
-
-(defvar-local completion-preview--original-cursor-type 'unset
+(defvar 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.")
 
@@ -262,9 +255,11 @@ set to the value of this option while the completion preview is visible."
   (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))
+  (when (completion-preview--cursor-modified-p)
+    (when (window-live-p completion-preview--window)
+      (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
@@ -277,20 +272,31 @@ set to the value of this option while the completion preview is visible."
   :interactive nil
   (if completion-preview-active-mode
       (progn
-        (add-hook 'window-selection-change-functions
-                  #'completion-preview--window-selection-change nil t)
+        (add-hook 'post-command-hook #'completion-preview--cleanup -10)
         (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)
+        (setq completion-preview--window (selected-window)
+              completion-preview--buffer (current-buffer)))
+    (remove-hook 'post-command-hook #'completion-preview--cleanup)
+    (completion-preview--restore-cursor)
+    (setq completion-preview--window nil
+          completion-preview--buffer nil)
     (completion-preview-hide)))
 
+(defun completion-preview--cleanup ()
+  "Dismiss completion preview and restore cursor shape if window changed."
+  (unless (or (and (eq completion-preview--window (selected-window))
+                   (eq completion-preview--buffer (current-buffer)))
+              (and (eq completion-preview--window (minibuffer-selected-window))
+                   (eq completion-preview--buffer (window-buffer (minibuffer-selected-window)))))
+    (when (buffer-live-p completion-preview--buffer)
+      (with-current-buffer completion-preview--buffer
+        (completion-preview-active-mode -1)))
+    (completion-preview--restore-cursor)
+    (remove-hook 'post-command-hook 'completion-preview--cleanup)))
+
 (defvar completion-preview-completion-styles '(basic)
   "List of completion styles that Completion Preview mode uses.
 
@@ -452,17 +458,6 @@ 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))))
@@ -635,7 +630,7 @@ completion suggestion, and \\[completion-preview-prev-candidate] cycles
 backward."
   :lighter " CP"
   (if completion-preview-mode
-      (add-hook 'post-command-hook #'completion-preview--post-command nil t)
+      (add-hook 'post-command-hook #'completion-preview--post-command 10 t)
     (remove-hook 'post-command-hook #'completion-preview--post-command t)
     (completion-preview-active-mode -1)))
 
index 7d358d075191a570bf1c33d97a9c231f75b24d72..1b0ef28f54d167ae9b8d48aa20846dfccfcab517 100644 (file)
@@ -307,4 +307,21 @@ instead."
       (should exit-fn-called)
       (should (equal exit-fn-args '("foobar" finished))))))
 
+(ert-deftest completion-preview-cursor-type ()
+  "Test modification of `cursor-type' when completion preview is visible."
+  (with-temp-buffer
+    (setq-local completion-at-point-functions
+                (list
+                 (completion-preview-tests--capf
+                  '("foobar" "foobaz"))))
+    (insert "foo")
+    (let ((this-command 'self-insert-command))
+      (completion-preview--post-command))
+    (should (eq completion-preview-cursor-type
+                (window-parameter nil 'cursor-type)))
+    (should (completion-preview--cursor-modified-p))
+    (let ((this-command 'keyboard-quit))
+      (completion-preview--post-command))
+    (should (not (completion-preview--cursor-modified-p)))))
+
 ;;; completion-preview-tests.el ends here