New upstream version 4.10
Lev Lamberov
5 years ago
8 | 8 | - EMACS_VERSION=emacs-25.1 |
9 | 9 | - EMACS_VERSION=emacs-25.2 |
10 | 10 | - EMACS_VERSION=emacs-25.3 |
11 | - EMACS_VERSION=emacs-git-snapshot | |
11 | - EMACS_VERSION=emacs-26-pretest | |
12 | # For some reason emac-git-snapshot hangs forever | |
13 | # - EMACS_VERSION=emacs-git-snapshot | |
12 | 14 | - EMACS_VERSION=remacs-git-snapshot |
13 | 15 | |
14 | 16 | matrix: |
15 | 17 | allow_failures: |
18 | - env: EMACS_VERSION=emacs-26-pretest | |
16 | 19 | - env: EMACS_VERSION=remacs-git-snapshot |
17 | 20 | - env: EMACS_VERSION=emacs-git-snapshot |
18 | 21 | |
38 | 41 | |
39 | 42 | script: |
40 | 43 | - make compile |
41 | - make test | |
44 | # Don't send redundant coverage info | |
45 | - UNDERCOVER_CONFIG='((:send-report nil))' make test | |
46 | - make test-with-flx |
0 | ;; -*- mode: emacs-lisp -*- | |
1 | ||
2 | 0 | (source gnu) |
3 | 1 | (source melpa) |
4 | 2 | |
9 | 7 | |
10 | 8 | (development |
11 | 9 | (depends-on "flx-ido") |
12 | (depends-on "with-simulated-input" | |
13 | :git "https://github.com/DarwinAwardWinner/with-simulated-input.git" | |
14 | :files ("*.el")) | |
15 | (depends-on "buttercup") | |
10 | (depends-on "with-simulated-input" "2.2") | |
11 | (depends-on "buttercup" "1.9") | |
16 | 12 | (depends-on "undercover")) |
0 | 2018-04-24 Ryan C. Thompson <rct@thompsonclan.org> | |
1 | ||
2 | * ido-completing-read+.el (ido-cr+-update-dynamic-collection): | |
3 | Prevent unintended re-sorting of the completion list after dynamic | |
4 | updates. | |
5 | ||
6 | 2018-04-21 Ryan C. Thompson <rct@thompsonclan.org> | |
7 | ||
8 | * ido-completing-read+.el (ido-cr+-update-dynamic-collection): Fix | |
9 | accidental creation of circular lists, which could lead to errors | |
10 | or indefinite hangs (#151) | |
11 | ||
12 | 2018-02-16 Ryan C. Thompson <rct@thompsonclan.org> | |
13 | ||
14 | * ido-completing-read+.el: Add a temporary fix for a conflict with | |
15 | flx-ido in Emacs 26. | |
16 | ||
0 | 17 | 2017-08-19 Ryan C. Thompson <rct@thompsonclan.org> |
1 | 18 | |
2 | 19 | * ido-completing-read+.el (ido-cr+-maybe-update-blacklist): Fix a |
5 | 5 | |
6 | 6 | all: test |
7 | 7 | |
8 | # We run clean-elc because undercover.el doesn't support elc files | |
9 | test: | |
10 | cask clean-elc | |
11 | cask exec buttercup -L . | |
8 | # We run clean-elc first because undercover.el doesn't support elc | |
9 | # files. We run the tests first without loading flx-ido, and then with | |
10 | # it. We only send the coverage report when running the full test | |
11 | # suite. | |
12 | test: clean | |
13 | cask exec buttercup -L . tests | |
14 | ||
15 | test-with-flx: clean | |
16 | cask exec buttercup -l tests/setup-undercover.el -L . tests tests-with-flx-ido | |
17 | ||
18 | all-tests: test test-with-flx | |
12 | 19 | |
13 | 20 | compile: $(ELC_FILES) |
14 | 21 |
43 | 43 | ## Ido itself ## |
44 | 44 | |
45 | 45 | First, enable `ido-mode` and `ido-everywhere`. |
46 | ||
47 | (ido-mode 1) | |
48 | (ido-everywhere 1) | |
49 | ||
46 | ```elisp | |
47 | (ido-mode 1) | |
48 | (ido-everywhere 1) | |
49 | ``` | |
50 | 50 | ## ido-completing-read+ (this package) ## |
51 | 51 | |
52 | 52 | Install this package [from MELPA](http://melpa.org/#/ido-completing-read+) |
53 | 53 | and then turn on `ido-ubiquitous-mode`: |
54 | ||
55 | (require 'ido-completing-read+) | |
56 | (ido-ubiquitous-mode 1) | |
57 | ||
54 | ```elisp | |
55 | (require 'ido-completing-read+) | |
56 | (ido-ubiquitous-mode 1) | |
57 | ``` | |
58 | 58 | ## Smex ## |
59 | 59 | |
60 | 60 | Smex allows you to use ido for completion of commands in M-x, with |
62 | 62 | list. First install the [smex](https://github.com/nonsequitur/smex) |
63 | 63 | package, then follow the directions to load it and replace your normal |
64 | 64 | M-x key-binding with smex: |
65 | ||
66 | (require 'smex) ; Not needed if you use package.el | |
67 | (smex-initialize) ; Can be omitted. This might cause a (minimal) delay | |
68 | ; when Smex is auto-initialized on its first run. | |
69 | (global-set-key (kbd "M-x") 'smex) | |
70 | (global-set-key (kbd "M-X") 'smex-major-mode-commands) | |
71 | ;; This is your old M-x. | |
72 | (global-set-key (kbd "C-c C-c M-x") 'execute-extended-command) | |
73 | ||
65 | ```elisp | |
66 | (require 'smex) ; Not needed if you use package.el | |
67 | (smex-initialize) ; Can be omitted. This might cause a (minimal) delay | |
68 | ; when Smex is auto-initialized on its first run. | |
69 | (global-set-key (kbd "M-x") 'smex) | |
70 | (global-set-key (kbd "M-X") 'smex-major-mode-commands) | |
71 | ;; This is your old M-x. | |
72 | (global-set-key (kbd "C-c C-c M-x") 'execute-extended-command) | |
73 | ``` | |
74 | 74 | (These directions are the same ones given in the smex README file.) |
75 | 75 | |
76 | 76 | ## ido-yes-or-no ## |
77 | 77 | |
78 | 78 | If you want to use ido for yes-or-no questions, even though it's |
79 | 79 | massive overkill, install my [ido-yes-or-no package from MELPA](http://melpa.org/#/ido-yes-or-no), and then enable the mode: |
80 | ||
81 | (require 'ido-yes-or-no) | |
82 | (ido-yes-or-no-mode 1) | |
83 | ||
80 | ```elisp | |
81 | (require 'ido-yes-or-no) | |
82 | (ido-yes-or-no-mode 1) | |
83 | ``` | |
84 | 84 | ## ido for `describe-face` and certain other commands ## |
85 | 85 | |
86 | 86 | Some commands, such as `describe-face`, use `completing-read-multiple` |
91 | 91 | the [crm-custom](https://github.com/DarwinAwardWinner/crm-custom) |
92 | 92 | package [from MELPA](http://melpa.org/#/crm-custom), then enable the |
93 | 93 | mode: |
94 | ||
95 | (require 'crm-custom) | |
96 | (crm-custom-mode 1) | |
97 | ||
94 | ```elisp | |
95 | (require 'crm-custom) | |
96 | (crm-custom-mode 1) | |
97 | ``` | |
98 | 98 | Make sure to read and understand the FAQ entry below about the empty |
99 | 99 | entry at the beginning of the completion list before using this mode, |
100 | 100 | or using it will likely be very confusing. |
116 | 116 | called `icomplete-mode` that integrates with standard emacs completion |
117 | 117 | and adds some ido-like behavior. It is built in to emacs, so no |
118 | 118 | installation is necessary. Just load the file and enable the mode: |
119 | ||
120 | (require 'icomplete) | |
121 | (icomplete-mode 1) | |
122 | ||
119 | ```elisp | |
120 | (require 'icomplete) | |
121 | (icomplete-mode 1) | |
122 | ``` | |
123 | 123 | # Frequently asked questions # |
124 | 124 | |
125 | 125 | ## How does ido-ubiquitous-mode decide when to replace `completing-read`? <br/> Why don't some commands use ido completion? ## |
4 | 4 | ;; Filename: ido-completing-read+.el |
5 | 5 | ;; Author: Ryan Thompson |
6 | 6 | ;; Created: Sat Apr 4 13:41:20 2015 (-0700) |
7 | ;; Version: 4.7 | |
7 | ;; Version: 4.10 | |
8 | 8 | ;; Package-Requires: ((emacs "24.4") (cl-lib "0.5") (s "0.1") (memoize "1.1")) |
9 | 9 | ;; URL: https://github.com/DarwinAwardWinner/ido-completing-read-plus |
10 | 10 | ;; Keywords: ido, completion, convenience |
76 | 76 | ;; |
77 | 77 | ;;; Code: |
78 | 78 | |
79 | (defconst ido-completing-read+-version "4.7" | |
79 | (defconst ido-completing-read+-version "4.10" | |
80 | 80 | "Currently running version of ido-completing-read+. |
81 | 81 | |
82 | 82 | Note that when you update ido-completing-read+, this variable may |
181 | 181 | This allows ido-cr+ to update the set of completion candidates |
182 | 182 | dynamically.") |
183 | 183 | |
184 | (defvar ido-cr+-last-dynamic-update-text nil | |
185 | "The value of `ido-text' last time a dynamic update occurred.") | |
186 | ||
184 | 187 | (defvar ido-cr+-dynamic-update-idle-time 0.25 |
185 | 188 | "Time to wait before updating dynamic completion list.") |
186 | 189 | |
205 | 208 | |
206 | 209 | These are used for falling back to `completing-read-default'.") |
207 | 210 | |
208 | (defvar ido-cr+-all-completions-memoized nil | |
211 | (defvar ido-cr+-all-completions-memoized 'all-completions | |
209 | 212 | "Memoized version of `all-completions'. |
210 | 213 | |
211 | 214 | During completion with dynamic collection, this variable is set |
212 | 215 | to a memoized copy of `all-completions'.") |
213 | 216 | |
214 | (defvar ido-cr+-all-prefix-completions-memoized nil | |
217 | (defvar ido-cr+-all-prefix-completions-memoized 'ido-cr+-all-prefix-completions | |
215 | 218 | "Memoized version of `ido-cr+-all-prefix-completions'. |
216 | 219 | |
217 | 220 | During completion with dynamic collection, this variable is set |
510 | 513 | (when (and (not ido-cr+-assume-static-collection) |
511 | 514 | (functionp collection)) |
512 | 515 | collection)) |
516 | (ido-cr+-last-dynamic-update-text nil) | |
513 | 517 | ;; Only memoize if the collection is dynamic. |
514 | 518 | (ido-cr+-all-prefix-completions-memoized |
515 | 519 | (if ido-cr+-dynamic-collection |
827 | 831 | (cl-loop |
828 | 832 | for i from 0 upto (length string) |
829 | 833 | append (funcall |
830 | (or ido-cr+-all-completions-memoized | |
831 | 'all-completions) | |
834 | ido-cr+-all-completions-memoized | |
832 | 835 | (s-left i string) |
833 | 836 | collection |
834 | 837 | predicate) |
835 | 838 | into completion-list |
836 | 839 | finally return (delete-dups completion-list))) |
837 | ;; Otherwise, just call `all-completions' on the empty string to | |
838 | ;; get every possible completions for a static COLLECTION. | |
840 | ;; If COLLECTION is not dynamic, then just call `all-completions' | |
841 | ;; on the empty string, which will already return every possible | |
842 | ;; completion. | |
839 | 843 | (t |
840 | 844 | (all-completions "" collection predicate)))) |
841 | 845 | |
872 | 876 | (nreverse filtered-collection) |
873 | 877 | filtered-collection))) |
874 | 878 | |
879 | (defun ido-cr+-cyclicp (x) | |
880 | "Returns non-nill if X is a list containing a circular reference." | |
881 | (cl-loop | |
882 | for tortoise on x | |
883 | for hare on (cdr x) by #'cddr | |
884 | thereis (eq tortoise hare))) | |
885 | ||
886 | (defun ido-cr+-maybe-chop (items elem) | |
887 | "Like `ido-chop', but a no-op if ELEM is not in ITEMS. | |
888 | ||
889 | Normal `ido-chop' hangs infinitely in this case." | |
890 | (cl-loop | |
891 | with new-tail = () | |
892 | for remaining on items | |
893 | for next = (car remaining) | |
894 | if (equal next elem) | |
895 | return (nconc remaining new-tail) | |
896 | else collect next into new-tail | |
897 | finally return items)) | |
898 | ||
875 | 899 | (defun ido-cr+-update-dynamic-collection () |
876 | 900 | "Update the set of completions for a dynamic collection. |
877 | 901 | |
878 | 902 | This has no effect unless `ido-cr+-dynamic-collection' is non-nil." |
879 | (when (and (ido-cr+-active) | |
880 | ido-cr+-dynamic-collection) | |
881 | (let ((orig-ido-cur-list ido-cur-list)) | |
882 | (condition-case-unless-debug err | |
883 | (let* ((ido-text | |
884 | (buffer-substring-no-properties (minibuffer-prompt-end) | |
885 | ido-eoinput)) | |
886 | (predicate (nth 2 ido-cr+-orig-completing-read-args)) | |
887 | (first-match (car ido-matches)) | |
888 | (strings-to-check | |
889 | (cond | |
890 | ;; If no match, then we only check `ido-text' | |
891 | ((null first-match) | |
892 | (list ido-text)) | |
893 | ;; If `ido-text' is a prefix of `first-match', then we | |
894 | ;; only need to check `first-match' | |
895 | ((and first-match | |
896 | (s-prefix? ido-text first-match)) | |
897 | (list first-match)) | |
898 | ;; Otherwise we need to check both | |
899 | (t | |
900 | (list ido-text first-match)))) | |
901 | (new-completions | |
902 | (cl-loop | |
903 | for string in strings-to-check | |
904 | nconc | |
905 | (funcall | |
906 | (or ido-cr+-all-prefix-completions-memoized | |
907 | 'ido-cr+-all-prefix-completions) | |
908 | string ido-cr+-dynamic-collection predicate) | |
909 | into result | |
910 | finally return result))) | |
911 | ;; Put the previous first match back at the front if possible | |
912 | (when (and new-completions | |
913 | first-match | |
914 | (member first-match new-completions)) | |
915 | (setq new-completions | |
916 | (ido-chop new-completions first-match))) | |
917 | (when (not (equal new-completions ido-cur-list)) | |
918 | (when (and (bound-and-true-p flx-ido-mode) | |
919 | (functionp 'flx-ido-reset)) | |
920 | ;; Reset flx-ido since the set of completions has changed | |
921 | (funcall 'flx-ido-reset)) | |
922 | (setq ido-cur-list (delete-dups new-completions)) | |
923 | (when ido-cr+-active-restrictions | |
924 | (setq ido-cur-list (ido-cr+-apply-restrictions | |
925 | ido-cur-list | |
926 | ido-cr+-active-restrictions))) | |
927 | (ido-cr+--debug-message | |
928 | "Updated completion candidates for dynamic collection because `ido-text' changed to %S. `ido-cur-list' now has %s elements" | |
929 | ido-text (length ido-cur-list)) | |
930 | ;; Recompute matches with new completions | |
931 | (setq ido-rescan t) | |
932 | (ido-set-matches) | |
933 | ;; Rebuild the completion display unless ido is already planning | |
934 | ;; to do it anyway | |
935 | (unless ido-cr+-exhibit-pending | |
936 | (ido-tidy) | |
937 | (ido-exhibit)))) | |
938 | (error | |
939 | (display-warning 'ido-cr+ | |
940 | (format | |
941 | "Disabling dynamic update due to error: %S" | |
942 | err)) | |
943 | ;; Reset any variables that might have been modified during | |
944 | ;; the failed update | |
945 | (setq ido-cur-list orig-ido-cur-list) | |
946 | ;; Prevent any further attempts at dynamic updating | |
947 | (setq ido-cr+-dynamic-collection nil))))) | |
903 | (when (and ido-cr+-dynamic-collection | |
904 | (ido-cr+-active)) | |
905 | ;; (cl-assert (not (ido-cr+-cyclicp ido-cur-list))) | |
906 | (let ((orig-ido-cur-list ido-cur-list) | |
907 | (ido-text | |
908 | (buffer-substring-no-properties (minibuffer-prompt-end) | |
909 | ido-eoinput))) | |
910 | ;; If current `ido-text' is equal to or a prefix of the previous | |
911 | ;; one, a dynamic update is not needed. | |
912 | (when (or (null ido-cr+-last-dynamic-update-text) | |
913 | (not (s-prefix? ido-text ido-cr+-last-dynamic-update-text))) | |
914 | (ido-cr+--debug-message "Doing a dynamic update because `ido-text' changed from %S to %S" | |
915 | ido-cr+-last-dynamic-update-text ido-text) | |
916 | (setq ido-cr+-last-dynamic-update-text ido-text) | |
917 | (condition-case-unless-debug err | |
918 | (let* ((predicate (nth 2 ido-cr+-orig-completing-read-args)) | |
919 | (first-match (car ido-matches)) | |
920 | (strings-to-check | |
921 | (cond | |
922 | ;; If no match, then we only check `ido-text' | |
923 | ((null first-match) | |
924 | (list ido-text)) | |
925 | ;; If `ido-text' is a prefix of `first-match', then we | |
926 | ;; only need to check `first-match' | |
927 | ((and first-match | |
928 | (s-prefix? ido-text first-match)) | |
929 | (list first-match)) | |
930 | ;; Otherwise we need to check both | |
931 | (t | |
932 | (list ido-text first-match)))) | |
933 | (new-completions | |
934 | (cl-loop | |
935 | for string in strings-to-check | |
936 | append | |
937 | (funcall | |
938 | ido-cr+-all-prefix-completions-memoized | |
939 | string ido-cr+-dynamic-collection predicate) | |
940 | into result | |
941 | finally return result))) | |
942 | ;; (cl-assert (not (ido-cr+-cyclicp new-completions))) | |
943 | (if (equal new-completions ido-cur-list) | |
944 | (ido-cr+--debug-message "Skipping dynamic update because the completion list did not change.") | |
945 | (when (and (bound-and-true-p flx-ido-mode) | |
946 | (functionp 'flx-ido-reset)) | |
947 | ;; Reset flx-ido since the set of completions has changed | |
948 | (funcall 'flx-ido-reset)) | |
949 | (setq ido-cur-list (delete-dups (append ido-cur-list new-completions))) | |
950 | (when ido-cr+-active-restrictions | |
951 | (setq ido-cur-list (ido-cr+-apply-restrictions | |
952 | ido-cur-list | |
953 | ido-cr+-active-restrictions))) | |
954 | (ido-cr+--debug-message | |
955 | "Updated completion candidates for dynamic collection. `ido-cur-list' now has %s elements" | |
956 | ido-text (length ido-cur-list)) | |
957 | ;; Recompute matches with new completions | |
958 | (let ((ido-rescan t)) | |
959 | (ido-set-matches)) | |
960 | (setq ido-rescan nil) | |
961 | ;; Put the pre-update first match (if any) back in | |
962 | ;; front | |
963 | (when (and first-match | |
964 | (not (equal first-match (car ido-matches))) | |
965 | (member first-match ido-matches)) | |
966 | (ido-cr+--debug-message "Restoring first match %S after dynamic update" first-match) | |
967 | (setq ido-matches (ido-chop ido-matches first-match))) | |
968 | ;; Rebuild the completion display unless ido is already planning | |
969 | ;; to do it anyway | |
970 | (unless ido-cr+-exhibit-pending | |
971 | (ido-tidy) | |
972 | (let ((ido-rescan nil)) | |
973 | (ido-exhibit))))) | |
974 | (error | |
975 | (display-warning 'ido-cr+ | |
976 | (format | |
977 | "Disabling dynamic update due to error: %S" | |
978 | err)) | |
979 | ;; Reset any variables that might have been modified during | |
980 | ;; the failed update | |
981 | (setq ido-cur-list orig-ido-cur-list) | |
982 | ;; Prevent any further attempts at dynamic updating | |
983 | (setq ido-cr+-dynamic-collection nil)))))) | |
948 | 984 | ;; Always cancel an active timer when this function is called. |
949 | 985 | (when ido-cr+-dynamic-update-timer |
950 | 986 | (cancel-timer ido-cr+-dynamic-update-timer) |
958 | 994 | (when ido-cr+-dynamic-update-timer |
959 | 995 | (cancel-timer ido-cr+-dynamic-update-timer) |
960 | 996 | (setq ido-cr+-dynamic-update-timer nil)) |
997 | (cl-assert (not (ido-cr+-cyclicp ido-cur-list))) | |
961 | 998 | (if (<= (length ido-matches) 1) |
962 | 999 | ;; If we've narrowed it down to zero or one matches, update |
963 | 1000 | ;; immediately. |
986 | 1023 | (apply oldfun args)) |
987 | 1024 | ;; Update `ido-eoinput' |
988 | 1025 | (setq ido-eoinput (point-max)) |
1026 | ;; Clear this var to force an update | |
1027 | (setq ido-cr+-last-dynamic-update-text nil) | |
989 | 1028 | ;; Now do update |
990 | 1029 | (ido-cr+-update-dynamic-collection)) |
991 | 1030 | ;; After maybe updating the dynamic collection, if there's still |
3 | 3 | |
4 | 4 | ;; Author: Ryan C. Thompson |
5 | 5 | ;; URL: https://github.com/DarwinAwardWinner/ido-ubiquitous |
6 | ;; Version: 4.7 | |
6 | ;; Version: 4.10 | |
7 | 7 | ;; Created: 2011-09-01 |
8 | 8 | ;; Keywords: convenience, completion, ido |
9 | 9 | ;; EmacsWiki: InteractivelyDoThings |
10 | ;; Package-Requires: ((ido-completing-read+ "4.7") (cl-lib "0.5")) | |
10 | ;; Package-Requires: ((ido-completing-read+ "4.10") (cl-lib "0.5")) | |
11 | 11 | ;; Filename: ido-ubiquitous.el |
12 | 12 | |
13 | 13 | ;; This file is NOT part of GNU Emacs. |
38 | 38 | ;; |
39 | 39 | ;;; Code: |
40 | 40 | |
41 | (defconst ido-ubiquitous-version "4.7" | |
41 | (defconst ido-ubiquitous-version "4.10" | |
42 | 42 | "Currently running version of ido-ubiquitous. |
43 | 43 | |
44 | 44 | Note that when you update ido-ubiquitous, this variable may not |
0 | 0 | ;;; -*- lexical-binding: t -*- |
1 | 1 | |
2 | (require 'undercover) | |
3 | (undercover "*.el" | |
4 | (:exclude "test-*.el")) | |
5 | ||
6 | 2 | (require 'ido) |
7 | (require 'flx-ido) | |
8 | 3 | (require 'minibuf-eldef) |
9 | 4 | (require 'ido-completing-read+) |
10 | 5 | (require 'buttercup) |
112 | 107 | (when (eq (car vars) 'quote) |
113 | 108 | (setq vars (eval vars))) |
114 | 109 | `(mapc #'unshadow-var ',vars)) |
110 | ||
111 | (defmacro with-temp-info-buffer (&rest body) | |
112 | "Create a temporary info buffer and exeluate BODY forms there." | |
113 | (declare (indent 0)) | |
114 | `(let ((temp-bufname (generate-new-buffer-name " *temp-info*"))) | |
115 | (unwind-protect | |
116 | (save-excursion | |
117 | (info nil (generate-new-buffer-name " *temp-info*")) | |
118 | ,@body) | |
119 | (when (get-buffer temp-bufname) | |
120 | (kill-buffer temp-bufname))))) | |
115 | 121 | |
116 | 122 | (describe "Within the `ido-completing-read+' package" |
117 | 123 | |
132 | 138 | ido-confirm-unique-completion |
133 | 139 | ido-enable-flex-matching |
134 | 140 | ido-enable-dot-prefix |
135 | flx-ido-mode | |
136 | 141 | (minibuffer-electric-default-mode t))) |
137 | 142 | |
138 | 143 | ;; Suppress all messages during tests |
564 | 569 | (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET" |
565 | 570 | (ido-completing-read+ |
566 | 571 | "Pick: " (collection-as-function collection) nil t nil nil (car collection))) |
567 | :to-equal "bbb-eee-ggg"))) | |
568 | ||
569 | (describe "with flx-ido-mode" | |
570 | (before-each | |
571 | (flx-ido-mode 1) | |
572 | (flx-ido-reset)) | |
573 | ||
574 | (it "should allow selection of dynamically-added completions" | |
575 | (expect | |
576 | (with-simulated-input "hello-w RET" | |
577 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
578 | :to-equal "hello-world") | |
579 | (expect 'ido-cr+-update-dynamic-collection | |
580 | :to-have-been-called)) | |
581 | ||
582 | (it "should allow ido flex-matching of dynamically-added completions" | |
583 | (expect | |
584 | (with-simulated-input "hello-ld RET" | |
585 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
586 | :to-equal | |
587 | "hello-world") | |
588 | (expect 'ido-cr+-update-dynamic-collection | |
589 | :to-have-been-called)) | |
590 | ||
591 | (it "should do a dynamic update when pressing TAB" | |
592 | (expect | |
593 | (with-simulated-input "h TAB -ld RET" | |
594 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
595 | :to-equal | |
596 | "hello-world") | |
597 | (expect 'ido-cr+-update-dynamic-collection | |
598 | :to-have-been-called)) | |
599 | ||
600 | (it "should do a dynamic update when idle" | |
601 | (expect | |
602 | (with-simulated-input | |
603 | '("h" | |
604 | (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time)) | |
605 | "-ld RET") | |
606 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
607 | :to-equal | |
608 | "hello-world") | |
609 | (expect 'ido-cr+-update-dynamic-collection | |
610 | :to-have-been-called)) | |
611 | ||
612 | (it "should do a dynamic update when there is only one match remaining" | |
613 | (expect | |
614 | (with-simulated-input "hell-ld RET" | |
615 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
616 | :to-equal | |
617 | "hello-world") | |
618 | (expect 'ido-cr+-update-dynamic-collection | |
619 | :to-have-been-called)) | |
620 | ||
621 | (it "should not exit with a unique match if new matches are dynamically added" | |
622 | (expect | |
623 | (with-simulated-input '("hell TAB -ld RET") | |
624 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
625 | :to-equal | |
626 | "hello-world") | |
627 | (expect 'ido-cr+-update-dynamic-collection | |
628 | :to-have-been-called)) | |
629 | ||
630 | (it "should exit with a match that is still unique after dynamic updating" | |
631 | (expect | |
632 | (with-simulated-input '("helic TAB") | |
633 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
634 | :to-equal | |
635 | "helicopter") | |
636 | (expect 'ido-cr+-update-dynamic-collection | |
637 | :to-have-been-called)) | |
638 | ||
639 | (it "should respect `ido-restrict-to-matches' when doing dynamic updates" | |
640 | (let ((collection | |
641 | (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg" | |
642 | "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg" | |
643 | "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg" | |
644 | "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh" | |
645 | "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh" | |
646 | "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh" | |
647 | "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii" | |
648 | "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii" | |
649 | "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii"))) | |
650 | ;; Test the internal function | |
651 | (expect | |
652 | (ido-cr+-apply-restrictions | |
653 | collection | |
654 | (list (cons nil "bbb") | |
655 | (cons nil "eee"))) | |
656 | :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii")) | |
657 | ||
658 | ;; First verify it without a dynamic collection | |
659 | (expect | |
660 | (with-simulated-input "eee C-SPC bbb C-SPC ggg RET" | |
661 | (ido-completing-read+ | |
662 | "Pick: " collection nil t nil nil (car collection))) | |
663 | :to-equal "bbb-eee-ggg") | |
664 | ;; Now test the same with a dynamic collection | |
665 | (expect | |
666 | (with-simulated-input "eee C-SPC bbb C-SPC ggg RET" | |
667 | (ido-completing-read+ | |
668 | "Pick: " (collection-as-function collection) nil t nil nil (car collection))) | |
669 | :to-equal "bbb-eee-ggg"))))) | |
572 | :to-equal "bbb-eee-ggg")))) | |
670 | 573 | |
671 | 574 | (describe "with unusual inputs" |
672 | 575 | (it "should accept a COLLECTION of symbols" |
1010 | 913 | (expect |
1011 | 914 | (with-simulated-input "RET" |
1012 | 915 | (ido-completing-read+ "Prompt: " 'def-nil-collection nil t)) |
1013 | :to-equal "blue")))))) | |
1014 | ||
1015 | ;; (defun ido-cr+-run-all-tests () | |
1016 | ;; (interactive) | |
1017 | ;; (ert "^ido-cr\\+-")) | |
916 | :to-equal "blue")))) | |
917 | ||
918 | ;; Test is currently disabled pending additional information | |
919 | (xit "should not hang or error when deleting characters in `org-refile' (issue #152)" | |
920 | (expect | |
921 | (progn | |
922 | (ido-ubiquitous-mode 1) | |
923 | (save-excursion | |
924 | (with-temp-buffer | |
925 | (org-mode) | |
926 | (insert (s-trim " | |
927 | * Heading 1 | |
928 | ** Subheading 1.1 | |
929 | ** Subheading 1.2 | |
930 | ** Subheading 1.3 | |
931 | * Heading 2 | |
932 | * Heading 3 | |
933 | ")) | |
934 | (goto-char (point-max)) | |
935 | ;; TODO Figure out what else needs to be set up to call | |
936 | ;; `org-refile' | |
937 | (with-simulated-input | |
938 | "Heading DEL DEL DEL DEL DEL RET" | |
939 | (command-execute 'org-refile))))) | |
940 | :not :to-throw))) | |
941 | ||
942 | (describe "regressions should not occur for" | |
943 | (it "issue #151: should not hang or error when cycling matches in `Info-menu'" | |
944 | (expect | |
945 | (progn | |
946 | (ido-ubiquitous-mode 1) | |
947 | (with-temp-info-buffer | |
948 | (with-simulated-input | |
949 | '("emacs" | |
950 | (ido-next-match) | |
951 | (wsi-simulate-idle-time 5) | |
952 | (ido-next-match) | |
953 | (wsi-simulate-idle-time 5) | |
954 | (ido-next-match) | |
955 | (wsi-simulate-idle-time 5) | |
956 | (ido-next-match) | |
957 | (wsi-simulate-idle-time 5) | |
958 | "RET") | |
959 | (command-execute 'Info-menu)))) | |
960 | :not :to-throw)) | |
961 | ||
962 | (it "issue #153: should preserve the selected item when doing a deferred dynamic update" | |
963 | (expect | |
964 | (with-simulated-input | |
965 | '("Emacs" | |
966 | (ido-next-match) | |
967 | (wsi-simulate-idle-time 5) | |
968 | "RET") | |
969 | (ido-completing-read+ | |
970 | "Choose: " | |
971 | (collection-as-function '("Emacs" "Emacs A" "Emacs B" "Emacs C")))) | |
972 | :to-equal "Emacs A")))) | |
1018 | 973 | |
1019 | 974 | ;;; test-ido-completing-read+.el ends here |
0 | ;;; -*- lexical-binding: t -*- | |
1 | ||
2 | ;; This file contains tests specifically for ido-cr+ interoperating | |
3 | ;; with flx-ido. These tests were split out from the main test file, | |
4 | ;; which unfortunately means that they brought a lot of the | |
5 | ;; scaffolding from the main test file with them. This might get | |
6 | ;; cleaned up at a later date. Putting these in a separate file is | |
7 | ;; necessary so that the main test suite can be both with and without | |
8 | ;; flx-ido loaded. | |
9 | ||
10 | (require 'ido) | |
11 | (require 'flx-ido) | |
12 | (require 'minibuf-eldef) | |
13 | (require 'ido-completing-read+) | |
14 | (require 'buttercup) | |
15 | (require 'cl-lib) | |
16 | (require 'with-simulated-input) | |
17 | ||
18 | (defun collection-as-function (collection) | |
19 | "Return a function equivalent to COLLECTION. | |
20 | ||
21 | The returned function will work equivalently to COLLECTION when | |
22 | passed to `all-completions' and `try-completion'." | |
23 | (completion-table-dynamic (lambda (string) (all-completions string collection)))) | |
24 | ||
25 | (defun shadow-var (var &optional temp-value) | |
26 | "Shadow the value of VAR. | |
27 | ||
28 | This will push the current value of VAR to VAR's | |
29 | `shadowed-values' property, and then set it to TEMP-VALUE. To | |
30 | reverse this process, call `unshadow-var' on VAR. Vars can | |
31 | be shadowed recursively, and must be unshadowed once for each | |
32 | shadowing in order to restore the original value. You can think | |
33 | of shadowing as dynamic binding with `let', but with manual | |
34 | control over when bindings start and end. | |
35 | ||
36 | If VAR is a Custom variable (see `custom-variable-p'), it will be | |
37 | set using `customize-set-variable', and if TEMP-VALUE is nil it | |
38 | will be replaces with VAR's standard value. | |
39 | ||
40 | Other variables will be set with `set-default', and a TEMP-VALUE | |
41 | of nil will not be treated specially. | |
42 | ||
43 | `shadow-var' only works on variables declared as special (i.e. | |
44 | using `defvar' or similar). It will not work on lexically bound | |
45 | variables." | |
46 | (unless (special-variable-p var) | |
47 | (error "Cannot shadow lexical var `%s'" var)) | |
48 | (let* ((use-custom (custom-variable-p var)) | |
49 | (setter (if use-custom 'customize-set-variable 'set-default)) | |
50 | (temp-value (or temp-value | |
51 | (and use-custom | |
52 | (eval (car (get var 'standard-value))))))) | |
53 | ;; Push the current value on the stack | |
54 | (push (symbol-value var) (get var 'shadowed-values)) | |
55 | (funcall setter var temp-value))) | |
56 | ||
57 | (defun var-shadowed-p (var) | |
58 | "Return non-nil if VAR is shadowed by `shadow-var'." | |
59 | ;; We don't actually want to return that list if it's non-nil. | |
60 | (and (get var 'shadowed-values) t)) | |
61 | ||
62 | (defun unshadow-var (var) | |
63 | "Reverse the last call to `shadow-var' on VAR." | |
64 | (if (var-shadowed-p var) | |
65 | (let* ((use-custom (custom-variable-p var)) | |
66 | (setter (if use-custom 'customize-set-variable 'set-default)) | |
67 | (value (pop (get var 'shadowed-values)))) | |
68 | (funcall setter var value)) | |
69 | (error "Var is not shadowed: %s" var))) | |
70 | ||
71 | (defun fully-unshadow-var (var) | |
72 | "Reverse *all* calls to `shadow-var' on VAR." | |
73 | (when (var-shadowed-p var) | |
74 | (let* ((use-custom (custom-variable-p var)) | |
75 | (setter (if use-custom 'customize-set-variable 'set-default)) | |
76 | (value (car (last (get var 'shadowed-values))))) | |
77 | (put var 'shadowed-values nil) | |
78 | (funcall setter var value)))) | |
79 | ||
80 | (defun fully-unshadow-all-vars (&optional vars) | |
81 | "Reverse *all* calls to `shadow-var' on VARS. | |
82 | ||
83 | If VARS is nil, unshadow *all* variables." | |
84 | (if vars | |
85 | (mapc #'fully-unshadow-var vars) | |
86 | (mapatoms #'fully-unshadow-var)) | |
87 | nil) | |
88 | ||
89 | (defmacro shadow-vars (varlist) | |
90 | "Shadow a list of vars with new values. | |
91 | ||
92 | VARLIST describes the variables to be shadowed with the same | |
93 | syntax as `let'. | |
94 | ||
95 | See `shadow-var'." | |
96 | (declare (indent 0)) | |
97 | (cl-loop | |
98 | with var = nil | |
99 | with value = nil | |
100 | for binding in varlist | |
101 | if (symbolp binding) | |
102 | do (setq var binding | |
103 | value nil) | |
104 | else | |
105 | do (setq var (car binding) | |
106 | value (cadr binding)) | |
107 | collect `(shadow-var ',var ,value) into exprs | |
108 | finally return `(progn ,@exprs))) | |
109 | ||
110 | (defmacro unshadow-vars (vars) | |
111 | "Un-shadow a list of VARS. | |
112 | ||
113 | This is a macro for consistency with `shadow-vars', but it will | |
114 | also accept a quoted list for the sake of convenience." | |
115 | (declare (indent 0)) | |
116 | (when (eq (car vars) 'quote) | |
117 | (setq vars (eval vars))) | |
118 | `(mapc #'unshadow-var ',vars)) | |
119 | ||
120 | (describe "Within the `ido-completing-read+' package" | |
121 | ||
122 | ;; Reset all of these variables to their standard values before each | |
123 | ;; test, saving the previous values for later restoration. | |
124 | (before-each | |
125 | (shadow-vars | |
126 | ((ido-mode t) | |
127 | (ido-ubiquitous-mode t) | |
128 | (ido-cr+-debug-mode t) | |
129 | ido-cr+-auto-update-blacklist | |
130 | ido-cr+-fallback-function | |
131 | ido-cr+-max-items | |
132 | ido-cr+-function-blacklist | |
133 | ido-cr+-function-whitelist | |
134 | ido-cr+-nil-def-alternate-behavior-list | |
135 | ido-cr+-replace-completely | |
136 | ido-confirm-unique-completion | |
137 | ido-enable-flex-matching | |
138 | ido-enable-dot-prefix | |
139 | flx-ido-mode | |
140 | (minibuffer-electric-default-mode t))) | |
141 | ||
142 | ;; Suppress all messages during tests | |
143 | (spy-on 'message)) | |
144 | ||
145 | ;; Restore the saved values after each test | |
146 | (after-each | |
147 | (fully-unshadow-all-vars)) | |
148 | ||
149 | (describe "the `ido-completing-read+' function" | |
150 | ||
151 | (describe "with dynamic collections" | |
152 | (before-all | |
153 | (setq my-dynamic-collection | |
154 | (completion-table-dynamic | |
155 | (lambda (text) | |
156 | (cond | |
157 | ;; Sub-completions for "hello" | |
158 | ((s-prefix-p "hello" text) | |
159 | '("hello" "hello-world" "hello-everyone" "hello-universe")) | |
160 | ;; Sub-completions for "goodbye" | |
161 | ((s-prefix-p "goodbye" text) | |
162 | '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe")) | |
163 | ;; General completions | |
164 | (t | |
165 | '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill"))))))) | |
166 | (after-all | |
167 | (setq my-dynamic-collection nil)) | |
168 | (before-each | |
169 | (setq ido-enable-flex-matching t | |
170 | ido-confirm-unique-completion nil) | |
171 | (spy-on 'ido-cr+-update-dynamic-collection | |
172 | :and-call-through)) | |
173 | ||
174 | (describe "with flx-ido-mode" | |
175 | (before-each | |
176 | (flx-ido-mode 1) | |
177 | (flx-ido-reset)) | |
178 | ||
179 | (it "should allow selection of dynamically-added completions" | |
180 | (expect | |
181 | (with-simulated-input "hello-w RET" | |
182 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
183 | :to-equal "hello-world") | |
184 | (expect 'ido-cr+-update-dynamic-collection | |
185 | :to-have-been-called)) | |
186 | ||
187 | (it "should allow ido flex-matching of dynamically-added completions" | |
188 | (expect | |
189 | (with-simulated-input "hello-ld RET" | |
190 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
191 | :to-equal | |
192 | "hello-world") | |
193 | (expect 'ido-cr+-update-dynamic-collection | |
194 | :to-have-been-called)) | |
195 | ||
196 | (it "should do a dynamic update when pressing TAB" | |
197 | (expect | |
198 | (with-simulated-input "h TAB -ld RET" | |
199 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
200 | :to-equal | |
201 | "hello-world") | |
202 | (expect 'ido-cr+-update-dynamic-collection | |
203 | :to-have-been-called)) | |
204 | ||
205 | (it "should do a dynamic update when idle" | |
206 | (expect | |
207 | (with-simulated-input | |
208 | '("h" | |
209 | (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time)) | |
210 | "-ld RET") | |
211 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
212 | :to-equal | |
213 | "hello-world") | |
214 | (expect 'ido-cr+-update-dynamic-collection | |
215 | :to-have-been-called)) | |
216 | ||
217 | (it "should do a dynamic update when there is only one match remaining" | |
218 | (expect | |
219 | (with-simulated-input "hell-ld RET" | |
220 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
221 | :to-equal | |
222 | "hello-world") | |
223 | (expect 'ido-cr+-update-dynamic-collection | |
224 | :to-have-been-called)) | |
225 | ||
226 | (it "should not exit with a unique match if new matches are dynamically added" | |
227 | (expect | |
228 | (with-simulated-input '("hell TAB -ld RET") | |
229 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
230 | :to-equal | |
231 | "hello-world") | |
232 | (expect 'ido-cr+-update-dynamic-collection | |
233 | :to-have-been-called)) | |
234 | ||
235 | (it "should exit with a match that is still unique after dynamic updating" | |
236 | (expect | |
237 | (with-simulated-input '("helic TAB") | |
238 | (ido-completing-read+ "Say something: " my-dynamic-collection)) | |
239 | :to-equal | |
240 | "helicopter") | |
241 | (expect 'ido-cr+-update-dynamic-collection | |
242 | :to-have-been-called)) | |
243 | ||
244 | (it "should respect `ido-restrict-to-matches' when doing dynamic updates" | |
245 | (let ((collection | |
246 | (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg" | |
247 | "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg" | |
248 | "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg" | |
249 | "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh" | |
250 | "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh" | |
251 | "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh" | |
252 | "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii" | |
253 | "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii" | |
254 | "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii"))) | |
255 | ;; Test the internal function | |
256 | (expect | |
257 | (ido-cr+-apply-restrictions | |
258 | collection | |
259 | (list (cons nil "bbb") | |
260 | (cons nil "eee"))) | |
261 | :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii")) | |
262 | ||
263 | ;; First verify it without a dynamic collection | |
264 | (expect | |
265 | (with-simulated-input "eee C-SPC bbb C-SPC ggg RET" | |
266 | (ido-completing-read+ | |
267 | "Pick: " collection nil t nil nil (car collection))) | |
268 | :to-equal "bbb-eee-ggg") | |
269 | ;; Now test the same with a dynamic collection | |
270 | (expect | |
271 | (with-simulated-input "eee C-SPC bbb C-SPC ggg RET" | |
272 | (ido-completing-read+ | |
273 | "Pick: " (collection-as-function collection) nil t nil nil (car collection))) | |
274 | :to-equal "bbb-eee-ggg"))))))) | |
275 | ||
276 | ;;; test-ido-completing-read+.el ends here |