Package: emacs;
Reported by: Rahul Martim Juliato <rahuljuliato <at> gmail.com>
Date: Fri, 24 Jan 2025 02:52:02 UTC
Severity: wishlist
Tags: patch
Done: Eli Zaretskii <eliz <at> gnu.org>
Bug is archived. No further changes may be made.
View this message in rfc822 format
From: Rahul Martim Juliato <rahuljuliato <at> gmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 75794 <at> debbugs.gnu.org Subject: bug#75794: [PATCH] feat(icomplete): markers and vertical alignment Date: Tue, 04 Mar 2025 11:03:52 -0300
[Message part 1 (text/plain, inline)]
Great news Eli!!! It took me a while, but I believe I’ve figured it out! I made a video comparing the behavior before and after applying this patch. I think a visual demonstration is more effective than words alone, as it highlights the many visual improvements. Here’s the video link: https://www.youtube.com/watch?v=ni0l6PruJEs This patch introduces two new features that improve icomplete-vertical-mode, which are explained below: 1.) Improve feature provided by `icomplete-in-buffer'. If user, besides setting `icomplete-in-buffer' to t, also set the new `icomplete-vertical-in-buffer-adjust-list' to t, the following are fixed/ improved: Without the new `icomplete-vertical-in-buffer-adjust-list': * wrapped lines - completion candidates on different columns always shows candidates at column 0 * wrapped lines - completion candidates on different lines always shows candidates at column 0 * wrapped lines - completion candidates close to the end of buffer won't be printed * truncated lines - completion candidates on different columns always shows candidates at column 0 * truncated lines - completion candidates on horizontally scrolled windows won't appear on buffer as they're on column 0 * truncated lines - completion candidates close to the end of buffer wont be shown With the new `icomplete-vertical-in-buffer-adjust-list': * wrapped lines - fix : completion candidates on different columns will always be printed under the cursor * wrapped lines - feature: completion candidates on different columns close to the end of the buffer will adjust so they stay visible * wrapped lines - fix: : completion candidates on different lines always be printed under the cursor * wrapped lines - fix : if icomplete-prospects-height won't fit from current line to the end of vertical space, our window will be scrolled so we have at least this amount of lines. This ensures our candidates list is always visible * truncated lines - fix : completion candidates on different columns will always be printed under the cursor * truncated lines - feature: completion candidates on different columns close to the end of the buffer will adjust so they stay visible even when we scroll horizontally * truncated lines - feature: completion candidates on horizontally scrolled windows will be printed under the cursor * wrapped lines - feature: if icomplete-prospects-height won't fit from current line to the end of vertical space, our window will be scrolled so we have at least this amount of lines. This ensures our candidates list is always visible * from wrapped - feature: if we are on wrapped lines and manually horiontal scroll, the lines to truncated will become automatically truncated, in this case, all the features above still works from either mode (wrapped or truncated). Attached is the latest (and hopefully final) version of this patch.
[0001-Enhance-icomplete-vertical-mode.patch (text/x-patch, inline)]
From 22fc5e56cd57c5e23b39f6de91831e73ae648515 Mon Sep 17 00:00:00 2001 From: Rahul Martim Juliato <rahul.juliato <at> gmail.com> Date: Tue, 4 Mar 2025 01:35:32 -0300 Subject: [PATCH] Enhance 'icomplete-vertical-mode' New user options have been added to enhance 'icomplete-vertical-mode': + 'icomplete-vertical-in-buffer-adjust-list': Aligns in-buffer completion to the original cursor column. + 'icomplete-vertical-render-prefix-indicator': When enabled, adds a prefix indicator to completion candidates. + 'icomplete-vertical-selected-prefix-indicator': Specifies the prefix string for the selected candidate. + 'icomplete-vertical-unselected-prefix-indicator': Specifies the prefix string for unselected candidates. New faces introduced: + 'icomplete-vertical-selected-prefix-indicator-face': Controls the appearance of the selected candidate prefix. + 'icomplete-vertical-unselected-prefix-indicator-face': Controls the appearance of unselected candidate prefixes. * etc/NEWS: Document the new user options and faces. * lisp/icomplete.el(icomplete-vertical-mode): Implement the new options --- etc/NEWS | 19 ++++++++ lisp/icomplete.el | 121 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 67f9ed84bdf..7d180b5b04d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -268,6 +268,25 @@ return value windows whose buffers share their text with BUFFER-OR-NAME. With such an entry, 'display-buffer-reuse-window' may also choose a window whose buffer shares text with the buffer to display. ++++ +*** New user options for 'icomplete-vertical-mode'. +New user options have been added to enhance 'icomplete-vertical-mode': + + - 'icomplete-vertical-in-buffer-adjust-list': Aligns in-buffer + completion to the original cursor column. + - 'icomplete-vertical-render-prefix-indicator': When enabled, adds a + prefix indicator to completion candidates. + - 'icomplete-vertical-selected-prefix-indicator': Specifies the prefix + string for the selected candidate. + - 'icomplete-vertical-unselected-prefix-indicator': Specifies the prefix + string for unselected candidates. + +New faces: + + - 'icomplete-vertical-selected-prefix-indicator-face': Controls the + appearance of the selected candidate prefix. + - 'icomplete-vertical-unselected-prefix-indicator-face': Controls the + appearance of unselected candidate prefixes. ** Frames diff --git a/lisp/icomplete.el b/lisp/icomplete.el index c58bffbb36b..246da70a8ba 100644 --- a/lisp/icomplete.el +++ b/lisp/icomplete.el @@ -115,6 +115,18 @@ Otherwise this should be a list of the completion tables (e.g., "Face used by `icomplete-vertical-mode' for the section title." :version "28.1") +(defface icomplete-vertical-selected-prefix-indicator-face + '((t :inherit font-lock-keyword-face :weight bold :foreground "cyan")) + "Face used for the prefix set by `icomplete-vertical-selected-prefix-indicator'." + :group 'icomplete + :version "31.1") + +(defface icomplete-vertical-unselected-prefix-indicator-face + '((t :inherit font-lock-keyword-face :weight normal :foreground "gray")) + "Face used for the prefix set by `icomplete-vertical-unselected-prefix-indicator'." + :group 'icomplete + :version "31.1") + ;;;_* User Customization variables (defcustom icomplete-prospects-height 2 ;; We used to compute how many lines 100 characters would take in @@ -166,6 +178,46 @@ will constrain Emacs to a maximum minibuffer height of 3 lines when icompletion is occurring." :type 'hook) +(defcustom icomplete-vertical-in-buffer-adjust-list nil + "Control whether in-buffer completion should align the cursor position. +If this is t and `icomplete-in-buffer' is t, and `icomplete-vertical-mode' +is activated, the in-buffer vertical completions are shown aligned to the +cursor position when the completion started, not on the first column, as +the default behaviour." + :type 'boolean + :group 'icomplete + :version "31.1") + +(defcustom icomplete-vertical-render-prefix-indicator nil + "Control whether a indicator is added as a prefix to each candidate. +If this is t and `icomplete-vertical-mode' is activated, a indicator, +controlled by `icomplete-vertical-selected-prefix-indicator' is shown +as a prefix to the current under selection candidate, while the +remaining of the candidates will receive the indicator controlled +by `icomplete-vertical-unselected-prefix-indicator'." + :type 'boolean + :group 'icomplete + :version "31.1") + +(defcustom icomplete-vertical-selected-prefix-indicator "» " + "Prefix string used to mark the selected completion candidate. +If `icomplete-vertical-render-prefix-indicator' is t, the string +defined here is used as a prefix of the currently selected entry in the +list. It can be further customized by the face +`icomplete-vertical-selected-prefix-indicator-face'." + :type 'string + :group 'icomplete + :version "31.1") + +(defcustom icomplete-vertical-unselected-prefix-indicator " " + "Prefix string used on the unselected completion candidates. +If `icomplete-vertical-render-prefix-indicator' is t, the string +defined here is used as a prefix for all unselected entries in the list. +list. It can be further customized by the face +`icomplete-vertical-unselected-prefix-indicator-face'." + :type 'string + :group 'icomplete + :version "31.1") ;;;_* Initialization @@ -828,6 +880,60 @@ by `group-function''s second \"transformation\" protocol." else collect (list tr prefix suffix )) annotated))) +(defun icomplete-vertical--adjust-lines-for-column (lines buffer data) + "Adjust the LINES to align with the column in BUFFER based on DATA." + (if icomplete-vertical-in-buffer-adjust-list + (let* ((column (current-column)) + (prefix-indicator-width + (if icomplete-vertical-render-prefix-indicator + (max (length icomplete-vertical-selected-prefix-indicator) + (length icomplete-vertical-unselected-prefix-indicator)) + 0)) + (wrapped-line (with-current-buffer buffer + (save-excursion + (goto-char (car data)) + (beginning-of-line) + (count-screen-lines (point) (car data))))) + (window-width (+ (window-hscroll) (window-body-width))) + (longest-line-width (apply #'max (mapcar #'length lines))) + (spaces-to-add + (if (> wrapped-line 1) + (- column (* (- wrapped-line 1) (- window-width 5))) + column)) + (spaces-to-add-avoiding-scrolling + (if (>= (+ spaces-to-add longest-line-width prefix-indicator-width) window-width) + (- spaces-to-add longest-line-width) + spaces-to-add))) + + (dolist (l lines) + (add-text-properties + 0 1 `(display ,(concat (make-string spaces-to-add-avoiding-scrolling ?\s) (substring l 0 1))) + l)) + lines) + lines)) + +(defun icomplete-vertical--ensure-visible-lines-inside-buffer () + "Ensure the completion list is visible in regular buffers only. +Scrolls the screen to be at least `icomplete-prospects-height' real lines +away from the bottom. Counts wrapped lines as real lines." + (unless (minibufferp) + (let* ((window-height (window-body-height)) + (current-line (count-screen-lines (window-start) (point))) + (lines-to-bottom (- window-height current-line))) + (when (< lines-to-bottom icomplete-prospects-height) + (scroll-up (- icomplete-prospects-height lines-to-bottom)))))) + +(defun icomplete-vertical--add-indicator-to-selected (comp) + "Add indicators to the selected/unselected COMP completions." + (if (and icomplete-vertical-render-prefix-indicator + (get-text-property 0 'icomplete-selected comp)) + (concat (propertize icomplete-vertical-selected-prefix-indicator + 'face 'icomplete-vertical-selected-prefix-indicator-face) + comp) + (concat (propertize icomplete-vertical-unselected-prefix-indicator + 'face 'icomplete-vertical-unselected-prefix-indicator-face) + comp))) + (cl-defun icomplete--render-vertical (comps md &aux scroll-above scroll-below (total-space ; number of mini-window lines available @@ -843,12 +949,17 @@ by `group-function''s second \"transformation\" protocol." ;; - both nil, there is no manual scroll; ;; - both non-nil, there is a healthy manual scroll that doesn't need ;; to be readjusted (user just moved around the minibuffer, for - ;; example)l + ;; example); ;; - non-nil and nil, respectively, a refiltering took place and we ;; may need to readjust them to the new filtered `comps'. + (when (and icomplete-scroll + (not icomplete--scrolled-completions) + (not icomplete--scrolled-past)) + (icomplete-vertical--ensure-visible-lines-inside-buffer)) (when (and icomplete-scroll icomplete--scrolled-completions (null icomplete--scrolled-past)) + (icomplete-vertical--ensure-visible-lines-inside-buffer) (cl-loop with preds for (comp . rest) on comps when (equal comp (car icomplete--scrolled-completions)) @@ -903,13 +1014,14 @@ by `group-function''s second \"transformation\" protocol." when section collect (propertize section 'face 'icomplete-section) into lines-aux and count 1 into nsections-aux + for comp = (icomplete-vertical--add-indicator-to-selected comp) when (get-text-property 0 'icomplete-selected comp) do (add-face-text-property 0 (length comp) 'icomplete-selected-match 'append comp) collect (concat prefix - (make-string (- max-prefix-len (length prefix)) ? ) + (make-string (max 0 (- max-prefix-len (length prefix))) ? ) (completion-lazy-hilit comp) - (make-string (- max-comp-len (length comp)) ? ) + (make-string (max 0 (- max-comp-len (length comp))) ? ) suffix) into lines-aux finally (setq lines lines-aux @@ -924,6 +1036,9 @@ by `group-function''s second \"transformation\" protocol." ((> (length scroll-above) (length scroll-below)) nsections) (t (min (ceiling nsections 2) (length scroll-above)))) lines)) + (when icomplete--in-region-buffer + (setq lines (icomplete-vertical--adjust-lines-for-column + lines icomplete--in-region-buffer completion-in-region--data))) ;; At long last, render final string return value. This may still ;; kick out lines at the end. (concat " \n" -- 2.48.1
[Message part 3 (text/plain, inline)]
-- Rahul Martim Juliato
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.