diff options
| author | Jimmy Aguilar Mena | 2020-11-22 23:14:18 +0100 |
|---|---|---|
| committer | Jimmy Aguilar Mena | 2020-11-22 23:58:11 +0100 |
| commit | c7c47e78e6c6eaf9518dfc8c7291c5a65b075827 (patch) | |
| tree | df9ac2f1a72f90c67469a2667e4daa69fcee223f | |
| parent | 5dd563f053f2fd57b3115765f920ab5acea1d5a8 (diff) | |
| download | emacs-feature/completions-highlight-modifications.tar.gz emacs-feature/completions-highlight-modifications.zip | |
Try another approach even simpler.feature/completions-highlight-modifications
Perform all the operations directly in the completions buffer.
| -rw-r--r-- | lisp/zcomplete.el | 242 |
1 files changed, 85 insertions, 157 deletions
diff --git a/lisp/zcomplete.el b/lisp/zcomplete.el index aa88ad8e47a..b5af6099297 100644 --- a/lisp/zcomplete.el +++ b/lisp/zcomplete.el | |||
| @@ -61,17 +61,6 @@ | |||
| 61 | :version "28.1" | 61 | :version "28.1" |
| 62 | :group 'completion) | 62 | :group 'completion) |
| 63 | 63 | ||
| 64 | (defcustom zcomplete-autoselect nil | ||
| 65 | "Select first candidate without extra tab. | ||
| 66 | |||
| 67 | When this variable is nil an extra tab is required to select and | ||
| 68 | highlight the first candidate in the *Completions* buffer. When | ||
| 69 | the value is non-nil the candidate is selected every time the | ||
| 70 | buffer is shown and updated." | ||
| 71 | :type 'boolean | ||
| 72 | :group 'zcomplete | ||
| 73 | :version "28.1") | ||
| 74 | |||
| 75 | (defcustom zcomplete-set-suffix t | 64 | (defcustom zcomplete-set-suffix t |
| 76 | "Insert completion candidate in minibuffer | 65 | "Insert completion candidate in minibuffer |
| 77 | 66 | ||
| @@ -101,56 +90,35 @@ otherwise it goes to the next completion. " | |||
| 101 | (defvar zcomplete-overlay (make-overlay 0 0) | 90 | (defvar zcomplete-overlay (make-overlay 0 0) |
| 102 | "Overlay to use when `completion-highlight-mode' is enabled.") | 91 | "Overlay to use when `completion-highlight-mode' is enabled.") |
| 103 | 92 | ||
| 104 | (defvar minibuffer-tab-through-completions-function-save nil | ||
| 105 | "Saves the the original value of completion-in-minibuffer-scroll-window.") | ||
| 106 | |||
| 107 | ;; *Completions* side commands | 93 | ;; *Completions* side commands |
| 108 | (defun zcomplete-select-near () | 94 | (defun zcomplete-select-near () |
| 109 | "Move to and highlight closer item in the completion list." | 95 | "Move to and highlight closer item in the completion list." |
| 110 | (interactive "p") | 96 | (interactive "p") |
| 111 | 97 | (let ((point (point)) | |
| 112 | (next-completion -1) | 98 | (pmin (point-min)) |
| 113 | (next-completion 1) | 99 | (pmax (point-max)) |
| 114 | ;; Try to find the closest completion if not in one | 100 | prev next choice) |
| 115 | (cond | 101 | |
| 116 | ((eobp) (next-completion -1)) | 102 | ;; Try to find the closest completion if not in one |
| 117 | ((bobp) (next-completion 1))) | 103 | (if (get-text-property point 'mouse-face) |
| 118 | 104 | (unless isearch-mode ;; assert we are in the beginning | |
| 119 | (let* ((obeg (point)) | 105 | (next-completion -1) |
| 120 | (oend (next-single-property-change obeg 'mouse-face nil (point-max))) | 106 | (next-completion 1)) |
| 121 | (choice (buffer-substring-no-properties obeg oend))) | 107 | |
| 122 | 108 | (setq prev (previous-single-property-change (min pmax (1+ point)) 'mouse-face nil pmin)) | |
| 123 | (move-overlay zcomplete-overlay obeg oend) | 109 | (setq next (next-single-property-change point 'mouse-face nil pmax)) |
| 124 | (when zcomplete-set-suffix | 110 | (if (or (eobp) |
| 125 | (zcomplete--set-suffix choice)))) | 111 | (< (- point prev) (- next point))) |
| 126 | 112 | (next-completion -1) | |
| 127 | (defsubst zcomplete-completions-visible-p () | 113 | (next-completion 1))) |
| 128 | "Return t if *Completions* is visible." | 114 | |
| 129 | (and (windowp minibuffer-scroll-window) | 115 | ;; Select region |
| 130 | (window-live-p minibuffer-scroll-window) | 116 | (setq point (point)) |
| 131 | (eq t (frame-visible-p (window-frame minibuffer-scroll-window))))) | 117 | (setq next (next-single-property-change point 'mouse-face nil (point-max))) |
| 132 | 118 | (setq choice (buffer-substring-no-properties point next)) | |
| 133 | (defun zcomplete-from-minibuffer (&optional command) | 119 | |
| 134 | (interactive) | 120 | (move-overlay zcomplete-overlay point next) |
| 135 | (and (zcomplete-completions-visible-p) | 121 | (zcomplete--set-suffix choice))) |
| 136 | (with-selected-window minibuffer-scroll-window | ||
| 137 | (when-let ((command (or command | ||
| 138 | (lookup-key (current-active-maps) | ||
| 139 | (this-single-command-keys)) | ||
| 140 | (lookup-key (current-active-maps) | ||
| 141 | (lookup-key local-function-key-map | ||
| 142 | (this-single-command-keys)))))) | ||
| 143 | (call-interactively command) | ||
| 144 | (run-hooks 'post-command-hook))))) | ||
| 145 | |||
| 146 | ;; Maybe this may be done with an advise? | ||
| 147 | (defun minibuffer-choose-completion () | ||
| 148 | "Execute `choose-completion' in *Completions*." | ||
| 149 | (interactive) | ||
| 150 | (if (and (zcomplete-completions-visible-p) | ||
| 151 | (overlay-buffer zcomplete-overlay)) | ||
| 152 | (call-interactively #'zcomplete-from-minibuffer) | ||
| 153 | (minibuffer-complete-and-exit))) | ||
| 154 | 122 | ||
| 155 | ;; General commands | 123 | ;; General commands |
| 156 | (defun zcomplete--set-suffix (choice) | 124 | (defun zcomplete--set-suffix (choice) |
| @@ -158,107 +126,82 @@ otherwise it goes to the next completion. " | |||
| 158 | It uses `completion-base-position' to determine the cursor | 126 | It uses `completion-base-position' to determine the cursor |
| 159 | position. If choice is the empty string the command removes the | 127 | position. If choice is the empty string the command removes the |
| 160 | suffix." | 128 | suffix." |
| 161 | (let* ((obase-position completion-base-position) | 129 | (when zcomplete-set-suffix |
| 162 | (minibuffer-window (active-minibuffer-window)) | 130 | (let* ((obase-position completion-base-position) |
| 163 | (minibuffer-buffer (window-buffer minibuffer-window)) | 131 | (minibuffer-window (active-minibuffer-window)) |
| 164 | (completion-no-auto-exit t)) | 132 | (minibuffer-buffer (window-buffer minibuffer-window)) |
| 165 | 133 | (completion-no-auto-exit t)) | |
| 166 | (with-selected-window minibuffer-window | 134 | |
| 167 | (let* ((prompt-end (minibuffer-prompt-end)) | 135 | (with-selected-window minibuffer-window |
| 168 | (cursor-pos (if obase-position | 136 | (let* ((prompt-end (minibuffer-prompt-end)) |
| 169 | (cadr obase-position) | 137 | (cursor-pos (if obase-position |
| 170 | (choose-completion-guess-base-position choice))) | 138 | (cadr obase-position) |
| 171 | (prefix-len (- cursor-pos prompt-end)) | 139 | (choose-completion-guess-base-position choice))) |
| 172 | (suffix (if (< prefix-len (length choice)) | 140 | (prefix-len (- cursor-pos prompt-end)) |
| 173 | (substring choice prefix-len) | 141 | (suffix (if (< prefix-len (length choice)) |
| 174 | "")) | 142 | (substring choice prefix-len) |
| 175 | (suffix-len (string-width suffix))) | 143 | "")) |
| 176 | 144 | (suffix-len (string-width suffix))) | |
| 177 | (choose-completion-string suffix minibuffer-buffer | 145 | |
| 178 | (list cursor-pos (point-max))) | 146 | (choose-completion-string suffix minibuffer-buffer |
| 179 | (add-face-text-property cursor-pos (+ cursor-pos suffix-len) 'shadow) | 147 | (list cursor-pos (point-max))) |
| 180 | (goto-char cursor-pos))))) | 148 | (add-face-text-property cursor-pos (+ cursor-pos suffix-len) 'shadow) |
| 181 | 149 | (goto-char cursor-pos)))))) | |
| 182 | (defun zcomplete--clear-suffix() | ||
| 183 | "Clear completion suffix if set." | ||
| 184 | (zcomplete--set-suffix "")) | ||
| 185 | |||
| 186 | (defvar zcomplete-minibuffer-map | ||
| 187 | (let ((map (make-sparse-keymap))) | ||
| 188 | (set-keymap-parent map minibuffer-local-must-match-map) | ||
| 189 | (dolist (key '(up down left right backtab)) | ||
| 190 | (define-key map `[(,key)] #'zcomplete-from-minibuffer)) | ||
| 191 | |||
| 192 | (define-key map [remap minibuffer-complete-and-exit] #'minibuffer-choose-completion) | ||
| 193 | map) | ||
| 194 | "Keymap used in minibuffer while *Completions* is active.") | ||
| 195 | 150 | ||
| 196 | (defvar zcomplete-completions-map | 151 | (defvar zcomplete-completions-map |
| 197 | (let ((map (make-sparse-keymap))) | 152 | (let ((map (make-sparse-keymap))) |
| 198 | (set-keymap-parent map completion-list-mode-map) | 153 | (define-key map [mouse-2] 'choose-completion) |
| 154 | (define-key map [follow-link] 'mouse-face) | ||
| 155 | (define-key map [down-mouse-2] nil) | ||
| 156 | (define-key map "\C-m" 'choose-completion) | ||
| 157 | (define-key map "\e\e\e" 'delete-completion-window) | ||
| 158 | (define-key map [left] 'previous-completion) | ||
| 159 | (define-key map [right] 'next-completion) | ||
| 160 | (define-key map [?\t] 'next-completion) | ||
| 161 | (define-key map [backtab] 'previous-completion) | ||
| 199 | (define-key map "\C-g" #'quit-window) | 162 | (define-key map "\C-g" #'quit-window) |
| 200 | map) | 163 | map) |
| 201 | "Keymap used in *Completions* while highlighting candidates.") | 164 | "Keymap used in *Completions* while highlighting candidates.") |
| 202 | 165 | ||
| 203 | (defun zcomplete--minibuffer-tab-through-completions () | 166 | (defun zcomplete--minibuffer-hook () |
| 204 | "Default action in `minibuffer-scroll-window' WINDOW. | ||
| 205 | This is called when *Completions* window is already visible and | ||
| 206 | should be assigned to completion-in-minibuffer-scroll-window." | ||
| 207 | (let ((window minibuffer-scroll-window)) | ||
| 208 | (with-current-buffer (window-buffer window) | ||
| 209 | (if zcomplete-tab-no-scroll | ||
| 210 | (zcomplete-from-minibuffer #'next-completion) | ||
| 211 | (if (pos-visible-in-window-p (point-max) window) ;; scroll o go to next | ||
| 212 | (if (pos-visible-in-window-p (point-min) window) | ||
| 213 | ;; If all completions are shown point-min and point-max | ||
| 214 | ;; are both visible. Then do the highlight. | ||
| 215 | (zcomplete-from-minibuffer #'next-completion) | ||
| 216 | ;; Else the buffer is too long, so better just scroll it to | ||
| 217 | ;; the beginning as default behavior. | ||
| 218 | (set-window-start window (point-min) nil)) | ||
| 219 | ;; Then point-max is not visible the buffer is too long and we | ||
| 220 | ;; can scroll. | ||
| 221 | (with-selected-window window (scroll-up))))))) | ||
| 222 | |||
| 223 | (defun zcomplete-maybe-close-completions () | ||
| 224 | "Close *Completions* buffer when the command is not in the map." | 167 | "Close *Completions* buffer when the command is not in the map." |
| 225 | (zcomplete--clear-suffix) | 168 | (zcomplete--set-suffix "") |
| 226 | (unless (lookup-key zcomplete-minibuffer-map | 169 | (unless (lookup-key minibuffer-local-must-match-map |
| 227 | (this-single-command-keys)) | 170 | (this-single-command-keys)) |
| 228 | (minibuffer-hide-completions))) | 171 | (minibuffer-hide-completions))) |
| 229 | 172 | ||
| 230 | (defun zcomplete--hide-completions-advise () | 173 | (defun zcomplete--completions-pre-hook () |
| 231 | "Function to advise minibuffer-hide-completions." | 174 | "Close *Completions* buffer when the command is not in the map." |
| 232 | (remove-hook 'pre-command-hook | 175 | (zcomplete--set-suffix "") |
| 233 | #'zcomplete-maybe-close-completions t)) | 176 | (when (eq this-command 'self-insert-command) |
| 234 | 177 | (call-interactively #'quit-window))) | |
| 235 | (defun zcomplete-setup () | 178 | |
| 179 | (defun zcomplete--hack (data context signal) | ||
| 180 | "Alternative to command-error-default-function. | ||
| 181 | This will exit the *Completions* if the error is buffer-read-only." | ||
| 182 | (if (eq (car data) 'buffer-read-only) | ||
| 183 | (call-interactively #'quit-window) | ||
| 184 | (command-error-default-function data context signal))) | ||
| 185 | |||
| 186 | (defun zcomplete--completions-setup-hook () | ||
| 236 | "Function to call when enabling the `completion-highlight-mode' mode. | 187 | "Function to call when enabling the `completion-highlight-mode' mode. |
| 237 | It is called when showing the *Completions* buffer." | 188 | It is called when showing the *Completions* buffer." |
| 238 | (delete-overlay zcomplete-overlay) | 189 | (delete-overlay zcomplete-overlay) |
| 239 | 190 | ||
| 240 | (with-current-buffer standard-output | 191 | ;; Add zcomplete-minibuffer-map bindings to minibuffer |
| 241 | (when (string= (buffer-name) "*Completions*") | 192 | (add-hook 'pre-command-hook #'zcomplete--minibuffer-hook nil t) |
| 242 | |||
| 243 | (add-hook 'pre-command-hook #'zcomplete--clear-suffix nil t) | ||
| 244 | (add-hook 'post-command-hook #'zcomplete-select-near nil t) | ||
| 245 | |||
| 246 | ;; Add zcomplete-completions-map to *Completions* | ||
| 247 | (use-local-map (make-composed-keymap | ||
| 248 | zcomplete-completions-map (current-local-map))) | ||
| 249 | 193 | ||
| 250 | ;; Autoselect candidate if enabled | 194 | ;; After this commands are for Completions |
| 251 | (when zcomplete-autoselect | 195 | (call-interactively #'switch-to-completions) |
| 252 | (with-selected-window (get-buffer-window (current-buffer) 0) | 196 | (add-hook 'pre-command-hook #'zcomplete--completions-pre-hook nil t) |
| 253 | (next-completion 1) | 197 | (add-hook 'post-command-hook #'zcomplete-select-near nil t) |
| 254 | (zcomplete-select-near))))) | ||
| 255 | 198 | ||
| 256 | (add-hook 'pre-command-hook | 199 | (setq-local command-error-function #'zcomplete--hack) |
| 257 | #'zcomplete-maybe-close-completions nil t) | 200 | (setq-local mode-line-format nil) |
| 201 | (use-local-map zcomplete-completions-map) | ||
| 258 | 202 | ||
| 259 | ;; Add zcomplete-minibuffer-map bindings to minibuffer | 203 | ;; Autoselect candidate if enabled |
| 260 | (use-local-map (make-composed-keymap | 204 | (zcomplete-select-near)) |
| 261 | zcomplete-minibuffer-map (current-local-map)))) | ||
| 262 | 205 | ||
| 263 | ;;;###autoload | 206 | ;;;###autoload |
| 264 | (define-minor-mode zcomplete-mode | 207 | (define-minor-mode zcomplete-mode |
| @@ -269,24 +212,9 @@ It is called when showing the *Completions* buffer." | |||
| 269 | (if zcomplete-mode | 212 | (if zcomplete-mode |
| 270 | (progn | 213 | (progn |
| 271 | (overlay-put zcomplete-overlay 'face 'zcomplete) | 214 | (overlay-put zcomplete-overlay 'face 'zcomplete) |
| 215 | (add-hook 'completion-setup-hook #'zcomplete--completions-setup-hook t)) | ||
| 272 | 216 | ||
| 273 | (setq minibuffer-tab-through-completions-function-save | 217 | (remove-hook 'completion-setup-hook #'zcomplete--completions-setup-hook))) |
| 274 | minibuffer-tab-through-completions-function) | ||
| 275 | |||
| 276 | (setq minibuffer-tab-through-completions-function | ||
| 277 | #'zcomplete--minibuffer-tab-through-completions) | ||
| 278 | |||
| 279 | (add-hook 'completion-setup-hook #'zcomplete-setup t) | ||
| 280 | (advice-add 'minibuffer-hide-completions | ||
| 281 | :before #'zcomplete--hide-completions-advise)) | ||
| 282 | |||
| 283 | ;; Restore the default completion-in-minibuffer-scroll-window | ||
| 284 | (setq minibuffer-tab-through-completions-function | ||
| 285 | minibuffer-tab-through-completions-function-save) | ||
| 286 | |||
| 287 | (remove-hook 'completion-setup-hook #'zcomplete-setup) | ||
| 288 | (advice-remove 'minibuffer-hide-completions | ||
| 289 | #'zcomplete--hide-completions-advise))) | ||
| 290 | 218 | ||
| 291 | (provide 'zcomplete) | 219 | (provide 'zcomplete) |
| 292 | ;;; zcomplete.el ends here | 220 | ;;; zcomplete.el ends here |