diff options
| author | João Távora | 2021-08-19 00:48:26 +0100 |
|---|---|---|
| committer | João Távora | 2021-08-19 12:16:03 +0100 |
| commit | 2be8e2ffc9dff0b43cc6beaac9084791e2f62be6 (patch) | |
| tree | a0bdfa7f1d2361566df3e3d0cb7d21935798fa2a | |
| parent | 2c699b87c2e4341be30908368eda7237c34a0152 (diff) | |
| download | emacs-2be8e2ffc9dff0b43cc6beaac9084791e2f62be6.tar.gz emacs-2be8e2ffc9dff0b43cc6beaac9084791e2f62be6.zip | |
Section by 'group-function' in Icomplete and Fido's vertical modes
Fixes: bug#48545
* lisp/icomplete.el (icomplete--augment): Rewrite from icomplete--affixate.
(icomplete--render-vertical): Rework.
(icomplete--vertical-minibuffer-setup): Separator is hardcoded "\n", no
need to set.
| -rw-r--r-- | lisp/icomplete.el | 149 |
1 files changed, 94 insertions, 55 deletions
diff --git a/lisp/icomplete.el b/lisp/icomplete.el index 81fc6ff03ca..73aaa3196a9 100644 --- a/lisp/icomplete.el +++ b/lisp/icomplete.el | |||
| @@ -111,6 +111,9 @@ Otherwise this should be a list of the completion tables (e.g., | |||
| 111 | "Face used by `icomplete-vertical-mode' for the selected candidate." | 111 | "Face used by `icomplete-vertical-mode' for the selected candidate." |
| 112 | :version "24.4") | 112 | :version "24.4") |
| 113 | 113 | ||
| 114 | (defface icomplete-section '((t :inherit shadow :slant italic)) | ||
| 115 | "Face used by `icomplete-vertical-mode' for the section title.") | ||
| 116 | |||
| 114 | ;;;_* User Customization variables | 117 | ;;;_* User Customization variables |
| 115 | (defcustom icomplete-prospects-height 2 | 118 | (defcustom icomplete-prospects-height 2 |
| 116 | ;; We used to compute how many lines 100 characters would take in | 119 | ;; We used to compute how many lines 100 characters would take in |
| @@ -635,8 +638,7 @@ Usually run by inclusion in `minibuffer-setup-hook'." | |||
| 635 | "Setup the minibuffer for vertical display of completion candidates." | 638 | "Setup the minibuffer for vertical display of completion candidates." |
| 636 | (use-local-map (make-composed-keymap icomplete-vertical-mode-minibuffer-map | 639 | (use-local-map (make-composed-keymap icomplete-vertical-mode-minibuffer-map |
| 637 | (current-local-map))) | 640 | (current-local-map))) |
| 638 | (setq-local icomplete-separator "\n" | 641 | (setq-local icomplete-hide-common-prefix nil |
| 639 | icomplete-hide-common-prefix nil | ||
| 640 | ;; Ask `icomplete-completions' to return enough completions candidates. | 642 | ;; Ask `icomplete-completions' to return enough completions candidates. |
| 641 | icomplete-prospects-height 25 | 643 | icomplete-prospects-height 25 |
| 642 | redisplay-adhoc-scroll-in-resize-mini-windows nil)) | 644 | redisplay-adhoc-scroll-in-resize-mini-windows nil)) |
| @@ -745,14 +747,21 @@ See `icomplete-mode' and `minibuffer-setup-hook'." | |||
| 745 | (format icomplete-matches-format current total)))) | 747 | (format icomplete-matches-format current total)))) |
| 746 | (overlay-put icomplete-overlay 'after-string text)))))))) | 748 | (overlay-put icomplete-overlay 'after-string text)))))))) |
| 747 | 749 | ||
| 748 | (defun icomplete--affixate (md prospects) | 750 | (defun icomplete--augment (md prospects) |
| 749 | "Affixate PROSPECTS given completion metadata MD. | 751 | "Augment completion strings in PROSPECTS with completion metadata MD. |
| 750 | Return a list of (COMP PREFIX SUFFIX)." | 752 | Return a list of strings (COMP PREFIX SUFFIX SECTION). PREFIX |
| 751 | (let ((aff-fun (or (completion-metadata-get md 'affixation-function) | 753 | and SUFFIX, if non-nil are obtained from `affixation-function' or |
| 752 | (plist-get completion-extra-properties :affixation-function))) | 754 | `annotation-function' metadata. SECTION is obtained from |
| 753 | (ann-fun (or (completion-metadata-get md 'annotation-function) | 755 | `group-function'. Consecutive `equal' sections are avoided. |
| 754 | (plist-get completion-extra-properties :annotation-function)))) | 756 | COMP is the element in PROSPECTS or a transformation also given |
| 755 | (cond (aff-fun | 757 | by `group-function''s second \"transformation\" protocol." |
| 758 | (let* ((aff-fun (or (completion-metadata-get md 'affixation-function) | ||
| 759 | (plist-get completion-extra-properties :affixation-function))) | ||
| 760 | (ann-fun (or (completion-metadata-get md 'annotation-function) | ||
| 761 | (plist-get completion-extra-properties :annotation-function))) | ||
| 762 | (grp-fun (completion-metadata-get md 'group-function)) | ||
| 763 | (annotated | ||
| 764 | (cond (aff-fun | ||
| 756 | (funcall aff-fun prospects)) | 765 | (funcall aff-fun prospects)) |
| 757 | (ann-fun | 766 | (ann-fun |
| 758 | (mapcar | 767 | (mapcar |
| @@ -766,9 +775,24 @@ Return a list of (COMP PREFIX SUFFIX)." | |||
| 766 | suffix | 775 | suffix |
| 767 | (propertize suffix 'face 'completions-annotations))))) | 776 | (propertize suffix 'face 'completions-annotations))))) |
| 768 | prospects)) | 777 | prospects)) |
| 769 | (prospects)))) | 778 | (t (mapcar #'list prospects))))) |
| 770 | 779 | (if grp-fun | |
| 771 | (cl-defun icomplete--render-vertical (comps md &aux scroll-above scroll-below) | 780 | (cl-loop with section = nil |
| 781 | for (c prefix suffix) in annotated | ||
| 782 | for selectedp = (get-text-property 0 'icomplete-selected c) | ||
| 783 | for tr = (propertize (or (funcall grp-fun c t) c) | ||
| 784 | 'icomplete-selected selectedp) | ||
| 785 | if (not (equal section (setq section (funcall grp-fun c nil)))) | ||
| 786 | collect (list tr prefix suffix section) | ||
| 787 | else collect (list tr prefix suffix )) | ||
| 788 | annotated))) | ||
| 789 | |||
| 790 | (cl-defun icomplete--render-vertical | ||
| 791 | (comps md &aux scroll-above scroll-below | ||
| 792 | (total-space ; number of mini-window lines available | ||
| 793 | (1- (min | ||
| 794 | icomplete-prospects-height | ||
| 795 | (truncate (max-mini-window-lines) 1))))) | ||
| 772 | ;; Welcome to loopapalooza! | 796 | ;; Welcome to loopapalooza! |
| 773 | ;; | 797 | ;; |
| 774 | ;; First, be mindful of `icomplete-scroll' and manual scrolls. If | 798 | ;; First, be mindful of `icomplete-scroll' and manual scrolls. If |
| @@ -776,11 +800,11 @@ Return a list of (COMP PREFIX SUFFIX)." | |||
| 776 | ;; are: | 800 | ;; are: |
| 777 | ;; | 801 | ;; |
| 778 | ;; - both nil, there is no manual scroll; | 802 | ;; - both nil, there is no manual scroll; |
| 779 | ;; - both non-nil, there is a healthy manual scroll the doesn't need | 803 | ;; - both non-nil, there is a healthy manual scroll that doesn't need |
| 780 | ;; to be readjusted (user just moved around the minibuffer, for | 804 | ;; to be readjusted (user just moved around the minibuffer, for |
| 781 | ;; example)l | 805 | ;; example)l |
| 782 | ;; - non-nil and nil, respectively, a refiltering took place and we | 806 | ;; - non-nil and nil, respectively, a refiltering took place and we |
| 783 | ;; need attempt to readjust them to the new filtered `comps'. | 807 | ;; may need to readjust them to the new filtered `comps'. |
| 784 | (when (and icomplete-scroll | 808 | (when (and icomplete-scroll |
| 785 | icomplete--scrolled-completions | 809 | icomplete--scrolled-completions |
| 786 | (null icomplete--scrolled-past)) | 810 | (null icomplete--scrolled-past)) |
| @@ -802,52 +826,67 @@ Return a list of (COMP PREFIX SUFFIX)." | |||
| 802 | ;; positions. | 826 | ;; positions. |
| 803 | (cl-loop with preds = icomplete--scrolled-past | 827 | (cl-loop with preds = icomplete--scrolled-past |
| 804 | with succs = (cdr comps) | 828 | with succs = (cdr comps) |
| 805 | with max-lines = (1- (min | 829 | with space-above = (- total-space |
| 806 | icomplete-prospects-height | 830 | 1 |
| 807 | (truncate (max-mini-window-lines) 1))) | 831 | (cl-loop for (_ . r) on comps |
| 808 | with max-above = (- max-lines | 832 | repeat (truncate total-space 2) |
| 809 | 1 | 833 | while (listp r) |
| 810 | (cl-loop for (_ . r) on comps | 834 | count 1)) |
| 811 | repeat (truncate max-lines 2) | 835 | repeat total-space |
| 812 | while (listp r) | ||
| 813 | count 1)) | ||
| 814 | repeat max-lines | ||
| 815 | for neighbour = nil | 836 | for neighbour = nil |
| 816 | if (and preds (> max-above 0)) do | 837 | if (and preds (> space-above 0)) do |
| 817 | (push (setq neighbour (pop preds)) scroll-above) | 838 | (push (setq neighbour (pop preds)) scroll-above) |
| 818 | (cl-decf max-above) | 839 | (cl-decf space-above) |
| 819 | else if (consp succs) collect | 840 | else if (consp succs) collect |
| 820 | (setq neighbour (pop succs)) into scroll-below-aux | 841 | (setq neighbour (pop succs)) into scroll-below-aux |
| 821 | while neighbour | 842 | while neighbour |
| 822 | finally (setq scroll-below scroll-below-aux)) | 843 | finally (setq scroll-below scroll-below-aux)) |
| 823 | ;; Now figure out spacing and layout | 844 | ;; Halfway there... |
| 824 | ;; | 845 | (let* ((selected (propertize (car comps) 'icomplete-selected t)) |
| 825 | (cl-loop | 846 | (chosen (append scroll-above (list selected) scroll-below)) |
| 826 | with selected = (substring (car comps)) | 847 | (tuples (icomplete--augment md chosen)) |
| 827 | initially (add-face-text-property 0 (length selected) | 848 | max-prefix-len max-comp-len lines nsections) |
| 828 | 'icomplete-selected-match 'append selected) | 849 | (add-face-text-property 0 (length selected) |
| 829 | with torender = (nconc scroll-above (list selected) scroll-below) | 850 | 'icomplete-selected-match 'append selected) |
| 830 | with triplets = (icomplete--affixate md torender) | 851 | ;; Figure out parameters for horizontal spacing |
| 831 | initially (when (eq triplets torender) | 852 | (cl-loop |
| 832 | (cl-return-from icomplete--render-vertical | 853 | for (comp prefix) in tuples |
| 833 | (concat | 854 | maximizing (length prefix) into max-prefix-len-aux |
| 834 | " \n" | 855 | maximizing (length comp) into max-comp-len-aux |
| 835 | (mapconcat #'identity torender icomplete-separator)))) | 856 | finally (setq max-prefix-len max-prefix-len-aux |
| 836 | for (comp prefix) in triplets | 857 | max-comp-len max-comp-len-aux)) |
| 837 | maximizing (length prefix) into max-prefix-len | 858 | ;; Serialize completions and section titles into a list |
| 838 | maximizing (length comp) into max-comp-len | 859 | ;; of lines to render |
| 839 | finally return | 860 | (cl-loop |
| 840 | ;; Finally, render | 861 | for (comp prefix suffix section) in tuples |
| 841 | ;; | 862 | when section |
| 842 | (concat | 863 | collect (propertize section 'face 'icomplete-section) into lines-aux |
| 843 | " \n" | 864 | and count 1 into nsections-aux |
| 844 | (cl-loop for (comp prefix suffix) in triplets | 865 | when (get-text-property 0 'icomplete-selected comp) |
| 845 | concat prefix | 866 | do (add-face-text-property 0 (length comp) |
| 846 | concat (make-string (- max-prefix-len (length prefix)) ? ) | 867 | 'icomplete-selected-match 'append comp) |
| 847 | concat comp | 868 | collect (concat prefix |
| 848 | concat (make-string (- max-comp-len (length comp)) ? ) | 869 | (make-string (- max-prefix-len (length prefix)) ? ) |
| 849 | concat suffix | 870 | comp |
| 850 | concat icomplete-separator)))) | 871 | (make-string (- max-comp-len (length comp)) ? ) |
| 872 | suffix) | ||
| 873 | into lines-aux | ||
| 874 | finally (setq lines lines-aux | ||
| 875 | nsections nsections-aux)) | ||
| 876 | ;; Kick out some lines from the beginning due to extra sections. | ||
| 877 | ;; This hopes to keep the selected entry more or less in the | ||
| 878 | ;; middle of the dropdown-like widget when `icomplete-scroll' is | ||
| 879 | ;; t. Funky, but at least I didn't use `cl-loop' | ||
| 880 | (setq lines | ||
| 881 | (nthcdr | ||
| 882 | (cond ((<= (length lines) total-space) 0) | ||
| 883 | ((> (length scroll-above) (length scroll-below)) nsections) | ||
| 884 | (t (min (ceiling nsections 2) (length scroll-above)))) | ||
| 885 | lines)) | ||
| 886 | ;; At long last, render final string return value. This may still | ||
| 887 | ;; kick out lines at the end. | ||
| 888 | (concat " \n" | ||
| 889 | (cl-loop for l in lines repeat total-space concat l concat "\n")))) | ||
| 851 | 890 | ||
| 852 | ;;;_ > icomplete-completions (name candidates predicate require-match) | 891 | ;;;_ > icomplete-completions (name candidates predicate require-match) |
| 853 | (defun icomplete-completions (name candidates predicate require-match) | 892 | (defun icomplete-completions (name candidates predicate require-match) |