mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-26 07:33:47 +00:00
Try another approach even simpler.
Perform all the operations directly in the completions buffer.
This commit is contained in:
parent
5dd563f053
commit
c7c47e78e6
@ -61,17 +61,6 @@
|
||||
:version "28.1"
|
||||
:group 'completion)
|
||||
|
||||
(defcustom zcomplete-autoselect nil
|
||||
"Select first candidate without extra tab.
|
||||
|
||||
When this variable is nil an extra tab is required to select and
|
||||
highlight the first candidate in the *Completions* buffer. When
|
||||
the value is non-nil the candidate is selected every time the
|
||||
buffer is shown and updated."
|
||||
:type 'boolean
|
||||
:group 'zcomplete
|
||||
:version "28.1")
|
||||
|
||||
(defcustom zcomplete-set-suffix t
|
||||
"Insert completion candidate in minibuffer
|
||||
|
||||
@ -101,56 +90,35 @@ otherwise it goes to the next completion. "
|
||||
(defvar zcomplete-overlay (make-overlay 0 0)
|
||||
"Overlay to use when `completion-highlight-mode' is enabled.")
|
||||
|
||||
(defvar minibuffer-tab-through-completions-function-save nil
|
||||
"Saves the the original value of completion-in-minibuffer-scroll-window.")
|
||||
|
||||
;; *Completions* side commands
|
||||
(defun zcomplete-select-near ()
|
||||
"Move to and highlight closer item in the completion list."
|
||||
(interactive "p")
|
||||
(let ((point (point))
|
||||
(pmin (point-min))
|
||||
(pmax (point-max))
|
||||
prev next choice)
|
||||
|
||||
(next-completion -1)
|
||||
(next-completion 1)
|
||||
;; Try to find the closest completion if not in one
|
||||
(cond
|
||||
((eobp) (next-completion -1))
|
||||
((bobp) (next-completion 1)))
|
||||
;; Try to find the closest completion if not in one
|
||||
(if (get-text-property point 'mouse-face)
|
||||
(unless isearch-mode ;; assert we are in the beginning
|
||||
(next-completion -1)
|
||||
(next-completion 1))
|
||||
|
||||
(let* ((obeg (point))
|
||||
(oend (next-single-property-change obeg 'mouse-face nil (point-max)))
|
||||
(choice (buffer-substring-no-properties obeg oend)))
|
||||
(setq prev (previous-single-property-change (min pmax (1+ point)) 'mouse-face nil pmin))
|
||||
(setq next (next-single-property-change point 'mouse-face nil pmax))
|
||||
(if (or (eobp)
|
||||
(< (- point prev) (- next point)))
|
||||
(next-completion -1)
|
||||
(next-completion 1)))
|
||||
|
||||
(move-overlay zcomplete-overlay obeg oend)
|
||||
(when zcomplete-set-suffix
|
||||
(zcomplete--set-suffix choice))))
|
||||
;; Select region
|
||||
(setq point (point))
|
||||
(setq next (next-single-property-change point 'mouse-face nil (point-max)))
|
||||
(setq choice (buffer-substring-no-properties point next))
|
||||
|
||||
(defsubst zcomplete-completions-visible-p ()
|
||||
"Return t if *Completions* is visible."
|
||||
(and (windowp minibuffer-scroll-window)
|
||||
(window-live-p minibuffer-scroll-window)
|
||||
(eq t (frame-visible-p (window-frame minibuffer-scroll-window)))))
|
||||
|
||||
(defun zcomplete-from-minibuffer (&optional command)
|
||||
(interactive)
|
||||
(and (zcomplete-completions-visible-p)
|
||||
(with-selected-window minibuffer-scroll-window
|
||||
(when-let ((command (or command
|
||||
(lookup-key (current-active-maps)
|
||||
(this-single-command-keys))
|
||||
(lookup-key (current-active-maps)
|
||||
(lookup-key local-function-key-map
|
||||
(this-single-command-keys))))))
|
||||
(call-interactively command)
|
||||
(run-hooks 'post-command-hook)))))
|
||||
|
||||
;; Maybe this may be done with an advise?
|
||||
(defun minibuffer-choose-completion ()
|
||||
"Execute `choose-completion' in *Completions*."
|
||||
(interactive)
|
||||
(if (and (zcomplete-completions-visible-p)
|
||||
(overlay-buffer zcomplete-overlay))
|
||||
(call-interactively #'zcomplete-from-minibuffer)
|
||||
(minibuffer-complete-and-exit)))
|
||||
(move-overlay zcomplete-overlay point next)
|
||||
(zcomplete--set-suffix choice)))
|
||||
|
||||
;; General commands
|
||||
(defun zcomplete--set-suffix (choice)
|
||||
@ -158,107 +126,82 @@ otherwise it goes to the next completion. "
|
||||
It uses `completion-base-position' to determine the cursor
|
||||
position. If choice is the empty string the command removes the
|
||||
suffix."
|
||||
(let* ((obase-position completion-base-position)
|
||||
(minibuffer-window (active-minibuffer-window))
|
||||
(minibuffer-buffer (window-buffer minibuffer-window))
|
||||
(completion-no-auto-exit t))
|
||||
(when zcomplete-set-suffix
|
||||
(let* ((obase-position completion-base-position)
|
||||
(minibuffer-window (active-minibuffer-window))
|
||||
(minibuffer-buffer (window-buffer minibuffer-window))
|
||||
(completion-no-auto-exit t))
|
||||
|
||||
(with-selected-window minibuffer-window
|
||||
(let* ((prompt-end (minibuffer-prompt-end))
|
||||
(cursor-pos (if obase-position
|
||||
(cadr obase-position)
|
||||
(choose-completion-guess-base-position choice)))
|
||||
(prefix-len (- cursor-pos prompt-end))
|
||||
(suffix (if (< prefix-len (length choice))
|
||||
(substring choice prefix-len)
|
||||
""))
|
||||
(suffix-len (string-width suffix)))
|
||||
(with-selected-window minibuffer-window
|
||||
(let* ((prompt-end (minibuffer-prompt-end))
|
||||
(cursor-pos (if obase-position
|
||||
(cadr obase-position)
|
||||
(choose-completion-guess-base-position choice)))
|
||||
(prefix-len (- cursor-pos prompt-end))
|
||||
(suffix (if (< prefix-len (length choice))
|
||||
(substring choice prefix-len)
|
||||
""))
|
||||
(suffix-len (string-width suffix)))
|
||||
|
||||
(choose-completion-string suffix minibuffer-buffer
|
||||
(list cursor-pos (point-max)))
|
||||
(add-face-text-property cursor-pos (+ cursor-pos suffix-len) 'shadow)
|
||||
(goto-char cursor-pos)))))
|
||||
|
||||
(defun zcomplete--clear-suffix()
|
||||
"Clear completion suffix if set."
|
||||
(zcomplete--set-suffix ""))
|
||||
|
||||
(defvar zcomplete-minibuffer-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map minibuffer-local-must-match-map)
|
||||
(dolist (key '(up down left right backtab))
|
||||
(define-key map `[(,key)] #'zcomplete-from-minibuffer))
|
||||
|
||||
(define-key map [remap minibuffer-complete-and-exit] #'minibuffer-choose-completion)
|
||||
map)
|
||||
"Keymap used in minibuffer while *Completions* is active.")
|
||||
(choose-completion-string suffix minibuffer-buffer
|
||||
(list cursor-pos (point-max)))
|
||||
(add-face-text-property cursor-pos (+ cursor-pos suffix-len) 'shadow)
|
||||
(goto-char cursor-pos))))))
|
||||
|
||||
(defvar zcomplete-completions-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map completion-list-mode-map)
|
||||
(define-key map [mouse-2] 'choose-completion)
|
||||
(define-key map [follow-link] 'mouse-face)
|
||||
(define-key map [down-mouse-2] nil)
|
||||
(define-key map "\C-m" 'choose-completion)
|
||||
(define-key map "\e\e\e" 'delete-completion-window)
|
||||
(define-key map [left] 'previous-completion)
|
||||
(define-key map [right] 'next-completion)
|
||||
(define-key map [?\t] 'next-completion)
|
||||
(define-key map [backtab] 'previous-completion)
|
||||
(define-key map "\C-g" #'quit-window)
|
||||
map)
|
||||
"Keymap used in *Completions* while highlighting candidates.")
|
||||
|
||||
(defun zcomplete--minibuffer-tab-through-completions ()
|
||||
"Default action in `minibuffer-scroll-window' WINDOW.
|
||||
This is called when *Completions* window is already visible and
|
||||
should be assigned to completion-in-minibuffer-scroll-window."
|
||||
(let ((window minibuffer-scroll-window))
|
||||
(with-current-buffer (window-buffer window)
|
||||
(if zcomplete-tab-no-scroll
|
||||
(zcomplete-from-minibuffer #'next-completion)
|
||||
(if (pos-visible-in-window-p (point-max) window) ;; scroll o go to next
|
||||
(if (pos-visible-in-window-p (point-min) window)
|
||||
;; If all completions are shown point-min and point-max
|
||||
;; are both visible. Then do the highlight.
|
||||
(zcomplete-from-minibuffer #'next-completion)
|
||||
;; Else the buffer is too long, so better just scroll it to
|
||||
;; the beginning as default behavior.
|
||||
(set-window-start window (point-min) nil))
|
||||
;; Then point-max is not visible the buffer is too long and we
|
||||
;; can scroll.
|
||||
(with-selected-window window (scroll-up)))))))
|
||||
|
||||
(defun zcomplete-maybe-close-completions ()
|
||||
(defun zcomplete--minibuffer-hook ()
|
||||
"Close *Completions* buffer when the command is not in the map."
|
||||
(zcomplete--clear-suffix)
|
||||
(unless (lookup-key zcomplete-minibuffer-map
|
||||
(zcomplete--set-suffix "")
|
||||
(unless (lookup-key minibuffer-local-must-match-map
|
||||
(this-single-command-keys))
|
||||
(minibuffer-hide-completions)))
|
||||
|
||||
(defun zcomplete--hide-completions-advise ()
|
||||
"Function to advise minibuffer-hide-completions."
|
||||
(remove-hook 'pre-command-hook
|
||||
#'zcomplete-maybe-close-completions t))
|
||||
(defun zcomplete--completions-pre-hook ()
|
||||
"Close *Completions* buffer when the command is not in the map."
|
||||
(zcomplete--set-suffix "")
|
||||
(when (eq this-command 'self-insert-command)
|
||||
(call-interactively #'quit-window)))
|
||||
|
||||
(defun zcomplete-setup ()
|
||||
(defun zcomplete--hack (data context signal)
|
||||
"Alternative to command-error-default-function.
|
||||
This will exit the *Completions* if the error is buffer-read-only."
|
||||
(if (eq (car data) 'buffer-read-only)
|
||||
(call-interactively #'quit-window)
|
||||
(command-error-default-function data context signal)))
|
||||
|
||||
(defun zcomplete--completions-setup-hook ()
|
||||
"Function to call when enabling the `completion-highlight-mode' mode.
|
||||
It is called when showing the *Completions* buffer."
|
||||
(delete-overlay zcomplete-overlay)
|
||||
|
||||
(with-current-buffer standard-output
|
||||
(when (string= (buffer-name) "*Completions*")
|
||||
|
||||
(add-hook 'pre-command-hook #'zcomplete--clear-suffix nil t)
|
||||
(add-hook 'post-command-hook #'zcomplete-select-near nil t)
|
||||
|
||||
;; Add zcomplete-completions-map to *Completions*
|
||||
(use-local-map (make-composed-keymap
|
||||
zcomplete-completions-map (current-local-map)))
|
||||
|
||||
;; Autoselect candidate if enabled
|
||||
(when zcomplete-autoselect
|
||||
(with-selected-window (get-buffer-window (current-buffer) 0)
|
||||
(next-completion 1)
|
||||
(zcomplete-select-near)))))
|
||||
|
||||
(add-hook 'pre-command-hook
|
||||
#'zcomplete-maybe-close-completions nil t)
|
||||
|
||||
;; Add zcomplete-minibuffer-map bindings to minibuffer
|
||||
(use-local-map (make-composed-keymap
|
||||
zcomplete-minibuffer-map (current-local-map))))
|
||||
(add-hook 'pre-command-hook #'zcomplete--minibuffer-hook nil t)
|
||||
|
||||
;; After this commands are for Completions
|
||||
(call-interactively #'switch-to-completions)
|
||||
(add-hook 'pre-command-hook #'zcomplete--completions-pre-hook nil t)
|
||||
(add-hook 'post-command-hook #'zcomplete-select-near nil t)
|
||||
|
||||
(setq-local command-error-function #'zcomplete--hack)
|
||||
(setq-local mode-line-format nil)
|
||||
(use-local-map zcomplete-completions-map)
|
||||
|
||||
;; Autoselect candidate if enabled
|
||||
(zcomplete-select-near))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode zcomplete-mode
|
||||
@ -269,24 +212,9 @@ It is called when showing the *Completions* buffer."
|
||||
(if zcomplete-mode
|
||||
(progn
|
||||
(overlay-put zcomplete-overlay 'face 'zcomplete)
|
||||
(add-hook 'completion-setup-hook #'zcomplete--completions-setup-hook t))
|
||||
|
||||
(setq minibuffer-tab-through-completions-function-save
|
||||
minibuffer-tab-through-completions-function)
|
||||
|
||||
(setq minibuffer-tab-through-completions-function
|
||||
#'zcomplete--minibuffer-tab-through-completions)
|
||||
|
||||
(add-hook 'completion-setup-hook #'zcomplete-setup t)
|
||||
(advice-add 'minibuffer-hide-completions
|
||||
:before #'zcomplete--hide-completions-advise))
|
||||
|
||||
;; Restore the default completion-in-minibuffer-scroll-window
|
||||
(setq minibuffer-tab-through-completions-function
|
||||
minibuffer-tab-through-completions-function-save)
|
||||
|
||||
(remove-hook 'completion-setup-hook #'zcomplete-setup)
|
||||
(advice-remove 'minibuffer-hide-completions
|
||||
#'zcomplete--hide-completions-advise)))
|
||||
(remove-hook 'completion-setup-hook #'zcomplete--completions-setup-hook)))
|
||||
|
||||
(provide 'zcomplete)
|
||||
;;; zcomplete.el ends here
|
||||
|
Loading…
Reference in New Issue
Block a user