aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorStephen Berman2025-09-22 16:04:42 +0200
committerStephen Berman2025-09-22 16:04:42 +0200
commit77ca60b48d018425c95c110b4c736cddd2d1d336 (patch)
tree50bed0386450ddfafac63b0bd98739c47030da9a /test
parentaeadaf77488a85838547ed8253a2f0b017cf4774 (diff)
downloademacs-77ca60b48d018425c95c110b4c736cddd2d1d336.tar.gz
emacs-77ca60b48d018425c95c110b4c736cddd2d1d336.zip
Navigate *Completions* buffer based on 'completions-format'
This patch makes 'next-completion' and 'previous-completion' work in the vertical completions format analogously to how they work in the default horizontal format (bug#78959). It also fixes wrapping in the vertical format and confines navigation (including wrapping) in column-wise movement in the vertical format to the current line, analogously to how navigation (including wrapping) in line-wise movement in the horizontal format is confined to the current column. * doc/emacs/mini.texi (Completion): Fix several typos and improve wording is several places. (Completion Commands): Document navigation of the *Completions* buffer in the vertical format. Document the difference between format-sensitive movement and strictly column-wise or line-wise movement. Document 'minibuffer-complete-and-exit' and update the documentation of 'minibuffer-completion-auto-choose' and 'minibuffer-choose-completion'. Document the use of a numeric prefix argument with the navigation commands. (Completion Options): Rearrange and improve documentation of 'completions-sort', 'completions-format' and 'completion-auto-wrap', updating the latter to document the new behavior. * lisp/minibuffer.el (minibuffer-visible-completions-map): Rebind "<left>" to 'minibuffer-previous-column-completion' and "<right>" to 'minibuffer-next-column-completion'. (minibuffer-next-completion): Add check for whether completions format is vertical to decide whether to call 'next-line-completion' and replace calling 'next-completion' by 'next-column-completion'. (minibuffer-next-column-completion) (minibuffer-previous-column-completion): New commands. * lisp/simple.el (completion-list-mode-map): Rebind "<left>" to 'previous-column-completion' and "<right>" to 'next-column-completion'. (last-completion): Add handling for vertical completions format. (completion--move-to-candidate-end): Always move point to the position immediately after the last character of the completion candidate. This unifies the behavior, simplifies the implementation and facilitates implementing the improved navigation of the *Completions* buffer. (previous-column-completion, next-column-completion): New commands, replacing the previous definitions of 'previous-completion' and 'next-completion' to reflect their column-wise operation. Confine navigation (including wrapping) in vertical format to the current line. (previous-line-completion, next-line-completion): Implement line-wise navigation (including wrapping) through all completions in vertical format, not just those in the current column as in horiztonal format. Update doc strings. (next-completion, previous-completion): Redefine to call '{next,previous}-line-completion' when completions format is vertical and '{next,previous}-column-completion' otherwise. * test/lisp/minibuffer-tests.el (completions-format-navigation--tests): New function providing a template to define tests of the navigation and wrapping behavior with specified numbers of completion candidates. (completions-format-navigation-test-{2,3,4,5,10,15,16}): New tests.
Diffstat (limited to 'test')
-rw-r--r--test/lisp/minibuffer-tests.el295
1 files changed, 294 insertions, 1 deletions
diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el
index 6954643976a..27e9bbbefb4 100644
--- a/test/lisp/minibuffer-tests.el
+++ b/test/lisp/minibuffer-tests.el
@@ -734,10 +734,303 @@
734 (let ((completion-auto-wrap nil)) 734 (let ((completion-auto-wrap nil))
735 (first-completion) 735 (first-completion)
736 (next-line-completion 7) 736 (next-line-completion 7)
737 (should (equal "ac2" (get-text-property (point) 'completion--string))) 737 (should (equal "ac1" (get-text-property (point) 'completion--string)))
738 (previous-line-completion 7) 738 (previous-line-completion 7)
739 (should (equal "aa1" (get-text-property (point) 'completion--string)))))) 739 (should (equal "aa1" (get-text-property (point) 'completion--string))))))
740 740
741(defun completions-format-navigation--tests (n)
742 "Make tests for navigating buffer of N completion candidate.
743The tests check expected results of navigating with and without wrapping
744for combinations of the values of `completion-auto-wrap' and
745`completions-format' (see bug#78959 for motivation and discussion of the
746expected behavior). The tests are actually run by calling this
747function, with specific values of N (> 1 to have a \"*Completions*\"
748buffer), from functions defined by `ert-deftest.'"
749 (let* (
750 ;; Make list of N unique completions.
751 (letters (mapcar 'string (number-sequence 97 122)))
752 (gen-compl (lambda (x)
753 (let (comps)
754 (dotimes (_ x)
755 (push (concat (car comps) (pop letters)) comps))
756 (nreverse comps))))
757 (completions (funcall gen-compl n))
758
759 ;; Navigation tests.
760 ;; (i) For both horizontal and vertical formats.
761 (all-completions
762 (lambda (type)
763 (let ((next-fn (pcase type
764 ('any 'next-completion)
765 ('column 'next-column-completion)
766 ('line 'next-line-completion)))
767 (prev-fn (pcase type
768 ('any 'previous-completion)
769 ('column 'previous-column-completion)
770 ('line 'previous-line-completion))))
771 (completing-read-with-minibuffer-setup completions
772 (insert (car completions))
773 (minibuffer-completion-help)
774 (switch-to-completions)
775 ;; Sanity check that we're on first completion candidate.
776 (should
777 (equal (car completions)
778 (get-text-property (point) 'completion--string)))
779 ;; Double check.
780 (first-completion)
781 (should
782 (equal (car completions)
783 (get-text-property (point) 'completion--string)))
784 ;; Test moving from first to Ith next completion
785 ;; candidate (0<I<N-1).
786 (dolist (i (number-sequence 1 (1- n)))
787 (funcall next-fn i)
788 (should
789 (equal (nth i completions)
790 (get-text-property (point) 'completion--string)))
791 (if (< i (1- n))
792 (first-completion)
793 (last-completion)
794 (should
795 (equal (nth i completions)
796 (get-text-property (point) 'completion--string)))
797 (funcall next-fn 1)
798 (if completion-auto-wrap
799 ;; Wrap around to first completion candidate.
800 (should
801 (equal (car completions)
802 (get-text-property (point) 'completion--string)))
803 ;; No wrapping.
804 (should
805 (equal (nth i completions)
806 (get-text-property (point) 'completion--string))))))
807 (last-completion)
808 ;; Test moving from last to Ith previous completion
809 ;; candidate (0<I<N-1).
810 (dolist (i (number-sequence 1 (1- n)))
811 (funcall prev-fn i)
812 (should
813 (equal (nth (- n i 1) completions)
814 (get-text-property (point) 'completion--string)))
815 (if (< i (1- n))
816 (last-completion)
817 (first-completion)
818 (should
819 (equal (car completions)
820 (get-text-property (point) 'completion--string)))
821 (funcall prev-fn 1)
822 (if completion-auto-wrap
823 ;; Wrap around to last completion candidate.
824 (should
825 (equal (nth (1- n) completions)
826 (get-text-property (point) 'completion--string)))
827 ;; No wrapping.
828 (should
829 (equal (car completions)
830 (get-text-property (point)
831 'completion--string))))))))))
832
833 ;; (ii) Only for horizontal format.
834 (within-column
835 (lambda ()
836 (completing-read-with-minibuffer-setup completions
837 (insert (car completions))
838 (minibuffer-completion-help)
839 (switch-to-completions)
840 (while (not (eolp))
841 (completion--move-to-candidate-start)
842 (let* ((first (get-text-property (point) 'completion--string))
843 (pos (point))
844 (i 0)
845 last1 last2)
846 ;; Keep moving to next completion candidate in this
847 ;; column until we reach the last one, and then wrap
848 ;; back to the first candidate, if
849 ;; `completion-auto-wrap' is non-nil, otherwise stay
850 ;; on the last one.
851 (while (or (and completion-auto-wrap
852 (not (equal last1 first)))
853 (not (equal last1
854 (get-text-property
855 (point) 'completion--string))))
856 (setq last2 last1)
857 (next-line-completion 1)
858 (incf i)
859 ;; Set `last1' to either the first or last
860 ;; candidate, depending on the value of
861 ;; `completion-auto-wrap'.
862 (setq last1 (get-text-property (point) 'completion--string)))
863 (setq last1 last2
864 last2 nil)
865 (decf i)
866 (when completion-auto-wrap
867 (should (equal (get-text-property (point) 'completion--string)
868 first))
869 ;; Test wrapping from last to first line in this column.
870 (next-line-completion i) ; Move to last candidate.
871 (should (equal (get-text-property (point) 'completion--string)
872 last1)))
873 ;; Now keeping move from last to first completion
874 ;; candidate in this column.
875 (while (or (not (equal last2 first))
876 (not (equal last2
877 (get-text-property
878 (point) 'completion--string))))
879 (previous-line-completion 1)
880 (setq last2 (get-text-property (point) 'completion--string)))
881 ;; Test wrapping from first to last line in this column.
882 (when completion-auto-wrap
883 (should (equal (get-text-property (point) 'completion--string)
884 first))
885 (next-line-completion i)
886 (should (equal (get-text-property (point) 'completion--string)
887 last1)))
888 ;; Move to first candidate in next column to continue loop
889 (completion--move-to-candidate-end)
890 (unless (eolp)
891 (goto-char pos)
892 (next-column-completion 1)))))))
893
894 ;; (iii) Only for vertical format.
895 (within-line
896 (lambda ()
897 (completing-read-with-minibuffer-setup completions
898 (insert (car completions))
899 (minibuffer-completion-help)
900 (switch-to-completions)
901 (let ((one-col (save-excursion
902 (first-completion)
903 (completion--move-to-candidate-end)
904 (eolp))))
905 (while (not (eobp))
906 (let ((first (get-text-property (point) 'completion--string))
907 last pos)
908 ;; Test moving to next column in this line.
909 (while (not (eolp))
910 (next-column-completion 1)
911 (let ((next (get-text-property (point) 'completion--string)))
912 (should
913 ;; FIXME: tautology?
914 (equal (nth (seq-position completions next) completions)
915 next)))
916 ;; If this is the last completion in this line,
917 ;; exit the loop.
918 (when (or (> (current-column) 0)
919 (save-excursion (and (forward-line) (eobp)))
920 (unless one-col
921 (save-excursion
922 (and (progn
923 (completion--move-to-candidate-start)
924 (bolp))
925 (progn
926 (completion--move-to-candidate-end)
927 (eolp))))))
928 (completion--move-to-candidate-end)))
929 (backward-char)
930 (completion--move-to-candidate-start)
931 (setq last (get-text-property (point) 'completion--string)
932 pos (point))
933 ;; We're on the last column, so next move either
934 ;; wraps or stays put.
935 (next-column-completion 1)
936 (if completion-auto-wrap
937 ;; We wrapped around to first candidate in this line.
938 (progn
939 (should (bolp))
940 (should
941 (equal (get-text-property (point) 'completion--string)
942 first))
943 ;; Go back to last completion in this line for next test.
944 (goto-char (if one-col pos (pos-eol)))
945 (backward-char))
946 (should
947 (equal (get-text-property (point) 'completion--string)
948 last)))
949 ;; Test moving to previous column in this line.
950 (while (if one-col
951 (save-excursion
952 (forward-line -1)
953 (get-text-property (point) 'completion--string))
954 (not (bolp)))
955 (previous-column-completion 1)
956 (let ((prev (get-text-property (point) 'completion--string)))
957 (should
958 ;; FIXME: tautology?
959 (equal (nth (seq-position completions prev) completions)
960 prev))))
961 ;; We're on the first column, so next move either
962 ;; wraps or stay put.
963 (previous-column-completion 1)
964 (if completion-auto-wrap
965 ;; We wrapped around to last candidate in this line.
966 (progn
967 (completion--move-to-candidate-end)
968 (should (eolp))
969 (backward-char)
970 (should
971 (equal (get-text-property (point) 'completion--string)
972 last)))
973 ;; We stayed on the first candidate.
974 (should
975 (equal (get-text-property (point) 'completion--string)
976 first)))
977 (if one-col
978 (goto-char (point-max))
979 (forward-line)))))))))
980
981 ;; Run tests.
982 ;; Test navigation with wrapping...
983 (let ((completion-auto-wrap t))
984 ;; ...in horizontal format,
985 (let ((completions-format 'horizontal))
986 (funcall all-completions 'any)
987 (funcall all-completions 'column)
988 (funcall within-column))
989 ;; ...in vertical format.
990 (let ((completions-format 'vertical))
991 (funcall all-completions 'any)
992 (funcall all-completions 'line)
993 (funcall within-line)))
994 ;; Test navigation without wrapping...
995 (let ((completion-auto-wrap nil))
996 ;; ...in horizontal format,
997 (let ((completions-format 'horizontal))
998 (funcall all-completions 'any)
999 (funcall all-completions 'column)
1000 (funcall within-column))
1001 ;; ...in vertical format.
1002 (let ((completions-format 'vertical))
1003 (funcall all-completions 'any)
1004 (funcall all-completions 'line)
1005 (funcall within-line)))))
1006
1007;; (ert-deftest completions-format-navigation-test-1 ()
1008;; (completions-format-navigation--tests 1))
1009
1010(ert-deftest completions-format-navigation-test-2 ()
1011 (completions-format-navigation--tests 2))
1012
1013(ert-deftest completions-format-navigation-test-3 ()
1014 (completions-format-navigation--tests 3))
1015
1016(ert-deftest completions-format-navigation-test-4 ()
1017 (completions-format-navigation--tests 4))
1018
1019(ert-deftest completions-format-navigation-test-5 ()
1020 (completions-format-navigation--tests 5))
1021
1022(ert-deftest completions-format-navigation-test-9 ()
1023 (completions-format-navigation--tests 9))
1024
1025(ert-deftest completions-format-navigation-test-10 ()
1026 (completions-format-navigation--tests 10))
1027
1028(ert-deftest completions-format-navigation-test-15 ()
1029 (completions-format-navigation--tests 15))
1030
1031(ert-deftest completions-format-navigation-test-16 ()
1032 (completions-format-navigation--tests 16))
1033
741(ert-deftest completion-cycle () 1034(ert-deftest completion-cycle ()
742 (completing-read-with-minibuffer-setup '("aaa" "bbb" "ccc") 1035 (completing-read-with-minibuffer-setup '("aaa" "bbb" "ccc")
743 (let ((completion-cycle-threshold t)) 1036 (let ((completion-cycle-threshold t))