>From b5e1548e571c093e682996d0a5b32eb8fb4d7919 Mon Sep 17 00:00:00 2001 From: Philip Kaludercic Date: Thu, 9 Dec 2021 17:26:14 +0100 Subject: [PATCH 2/3] Allow for next-completion to wrap around the completion buffer * lisp/simple.el (completion-wrap-movement): Add new option. (previous-completion): Update docstring. (next-completion): Respect completion-wrap-movement. (switch-to-completions): Handle backwards completion by jumping to the end of the buffer. * lisp/minibuffer.el: (minibuffer-local-completion-map): Bind minibuffer-complete to backtab (completion--in-region-1): Handle backtab to scroll backwards --- lisp/minibuffer.el | 15 +++++--- lisp/simple.el | 92 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 28bd1df59a..112c609a0a 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1379,14 +1379,18 @@ completion--in-region-1 ;; and this command is repeated, scroll that window. ((and (window-live-p minibuffer-scroll-window) (eq t (frame-visible-p (window-frame minibuffer-scroll-window)))) - (let ((window minibuffer-scroll-window)) + (let ((window minibuffer-scroll-window) + (reverse (equal (this-command-keys) [backtab]))) (with-current-buffer (window-buffer window) - (if (pos-visible-in-window-p (point-max) window) - ;; If end is in view, scroll up to the beginning. - (set-window-start window (point-min) nil) + (if (pos-visible-in-window-p (if reverse (point-min) (point-max)) window) + ;; If end or beginning is in view, scroll up to the + ;; beginning or end respectively. + (if reverse + (set-window-point window (point-max)) + (set-window-start window (point-min) nil)) ;; Else scroll down one screen. (with-selected-window window - (scroll-up))) + (if reverse (scroll-down) (scroll-up)))) nil))) ;; If we're cycling, keep on cycling. ((and completion-cycling completion-all-sorted-completions) @@ -2651,6 +2655,7 @@ minibuffer-local-completion-map (let ((map (make-sparse-keymap))) (set-keymap-parent map minibuffer-local-map) (define-key map "\t" 'minibuffer-complete) + (define-key map [backtab] 'minibuffer-complete) ;; M-TAB is already abused for many other purposes, so we should find ;; another binding for it. ;; (define-key map "\e\t" 'minibuffer-force-complete) diff --git a/lisp/simple.el b/lisp/simple.el index d0e58575e1..2d81dc62f4 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -9027,38 +9027,68 @@ delete-completion-window (if (get-buffer-window buf) (select-window (get-buffer-window buf)))))) +(defcustom completion-wrap-movement t + "Non-nil means to wrap around when selecting completion options. +This affects the commands `next-completion' and +`previous-completion'." + :type 'boolean + :version "29.1" + :group 'completion) + (defun previous-completion (n) - "Move to the previous item in the completion list." + "Move to the previous item in the completion list. +With prefix argument N, move back N items (negative N means move +forward)." (interactive "p") (next-completion (- n))) (defun next-completion (n) "Move to the next item in the completion list. -With prefix argument N, move N items (negative N means move backward)." +With prefix argument N, move N items (negative N means move +backward)." (interactive "p") (let ((beg (point-min)) (end (point-max))) - (while (and (> n 0) (not (eobp))) - ;; If in a completion, move to the end of it. - (when (get-text-property (point) 'mouse-face) - (goto-char (next-single-property-change (point) 'mouse-face nil end))) - ;; Move to start of next one. - (unless (get-text-property (point) 'mouse-face) - (goto-char (next-single-property-change (point) 'mouse-face nil end))) - (setq n (1- n))) - (while (and (< n 0) (not (bobp))) - (let ((prop (get-text-property (1- (point)) 'mouse-face))) - ;; If in a completion, move to the start of it. - (when (and prop (eq prop (get-text-property (point) 'mouse-face))) - (goto-char (previous-single-property-change - (point) 'mouse-face nil beg))) - ;; Move to end of the previous completion. - (unless (or (bobp) (get-text-property (1- (point)) 'mouse-face)) - (goto-char (previous-single-property-change - (point) 'mouse-face nil beg))) - ;; Move to the start of that one. - (goto-char (previous-single-property-change - (point) 'mouse-face nil beg)) - (setq n (1+ n)))))) + (catch 'bound + (while (> n 0) + ;; If in a completion, move to the end of it. + (when (get-text-property (point) 'mouse-face) + (goto-char (next-single-property-change (point) 'mouse-face nil end))) + ;; If at the last completion option, wrap or skip to the + ;; minibuffer, if requested. + (when (and completion-wrap-movement (eobp)) + (if (and (member (this-command-keys) '("\t" [backtab])) + completion-auto-select) + (throw 'bound nil) + (goto-char (point-min)))) + ;; Move to start of next one. + (unless (get-text-property (point) 'mouse-face) + (goto-char (next-single-property-change (point) 'mouse-face nil end))) + (setq n (1- n))) + (while (< n 0) + (let ((prop (get-text-property (1- (point)) 'mouse-face))) + ;; If in a completion, move to the start of it. + (when (and prop (eq prop (get-text-property (point) 'mouse-face))) + (goto-char (previous-single-property-change + (point) 'mouse-face nil beg))) + ;; Move to end of the previous completion. + (unless (or (bobp) (get-text-property (1- (point)) 'mouse-face)) + (goto-char (previous-single-property-change + (point) 'mouse-face nil beg))) + ;; If at the first completion option, wrap or skip to the + ;; minibuffer, if requested. + (when (and completion-wrap-movement (bobp)) + (if (and (member (this-command-keys) '("\t" [backtab])) + completion-auto-select) + (progn + (goto-char (next-single-property-change (point) 'mouse-face nil end)) + (throw 'bound nil)) + (goto-char (point-max)))) + ;; Move to the start of that one. + (goto-char (previous-single-property-change + (point) 'mouse-face nil beg)) + (setq n (1+ n))))) + (when (/= 0 n) + (switch-to-minibuffer)))) (defun choose-completion (&optional event) "Choose the completion at point. @@ -9283,10 +9313,16 @@ switch-to-completions (get-buffer-window "*Completions*" 0))))) (when window (select-window window) - ;; In the new buffer, go to the first completion. - ;; FIXME: Perhaps this should be done in `minibuffer-completion-help'. - (when (bobp) - (next-completion 1))))) + (cond + ((and (memq this-command '(completion-at-point minibuffer-complete)) + (equal (this-command-keys) [backtab]) + (bobp)) + (goto-char (point-max)) + (previous-completion 1)) + ;; In the new buffer, go to the first completion. + ;; FIXME: Perhaps this should be done in `minibuffer-completion-help'. + ((bobp) + (next-completion 1)))))) (defun read-expression-switch-to-completions () "Select the completion list window while reading an expression." -- 2.30.2