aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Távora2021-08-19 00:48:26 +0100
committerJoão Távora2021-08-19 12:16:03 +0100
commit2be8e2ffc9dff0b43cc6beaac9084791e2f62be6 (patch)
treea0bdfa7f1d2361566df3e3d0cb7d21935798fa2a
parent2c699b87c2e4341be30908368eda7237c34a0152 (diff)
downloademacs-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.el149
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.
750Return a list of (COMP PREFIX SUFFIX)." 752Return a list of strings (COMP PREFIX SUFFIX SECTION). PREFIX
751 (let ((aff-fun (or (completion-metadata-get md 'affixation-function) 753and 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)))) 756COMP is the element in PROSPECTS or a transformation also given
755 (cond (aff-fun 757by `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)