From: Eshel Yaron Date: Sat, 27 Apr 2024 19:08:17 +0000 (+0200) Subject: Change cursor type when showing completion preview X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=8c9813c59de9261aa6459232ef089549a0264787;p=emacs.git Change cursor type when showing completion preview * 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. --- diff --git a/lisp/completion-preview.el b/lisp/completion-preview.el index ba81210e36a..4e2f013b57a 100644 --- a/lisp/completion-preview.el +++ b/lisp/completion-preview.el @@ -49,6 +49,16 @@ ;; 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))))