Codebase list ido-ubiquitous / e5b2921
New upstream version 4.10 Lev Lamberov 5 years ago
10 changed file(s) with 534 addition(s) and 235 deletion(s). Raw diff Collapse all Expand all
88 - EMACS_VERSION=emacs-25.1
99 - EMACS_VERSION=emacs-25.2
1010 - 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
1214 - EMACS_VERSION=remacs-git-snapshot
1315
1416 matrix:
1517 allow_failures:
18 - env: EMACS_VERSION=emacs-26-pretest
1619 - env: EMACS_VERSION=remacs-git-snapshot
1720 - env: EMACS_VERSION=emacs-git-snapshot
1821
3841
3942 script:
4043 - 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
20 (source gnu)
31 (source melpa)
42
97
108 (development
119 (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")
1612 (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
017 2017-08-19 Ryan C. Thompson <rct@thompsonclan.org>
118
219 * ido-completing-read+.el (ido-cr+-maybe-update-blacklist): Fix a
55
66 all: test
77
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
1219
1320 compile: $(ELC_FILES)
1421
4343 ## Ido itself ##
4444
4545 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 ```
5050 ## ido-completing-read+ (this package) ##
5151
5252 Install this package [from MELPA](http://melpa.org/#/ido-completing-read+)
5353 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 ```
5858 ## Smex ##
5959
6060 Smex allows you to use ido for completion of commands in M-x, with
6262 list. First install the [smex](https://github.com/nonsequitur/smex)
6363 package, then follow the directions to load it and replace your normal
6464 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 ```
7474 (These directions are the same ones given in the smex README file.)
7575
7676 ## ido-yes-or-no ##
7777
7878 If you want to use ido for yes-or-no questions, even though it's
7979 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 ```
8484 ## ido for `describe-face` and certain other commands ##
8585
8686 Some commands, such as `describe-face`, use `completing-read-multiple`
9191 the [crm-custom](https://github.com/DarwinAwardWinner/crm-custom)
9292 package [from MELPA](http://melpa.org/#/crm-custom), then enable the
9393 mode:
94
95 (require 'crm-custom)
96 (crm-custom-mode 1)
97
94 ```elisp
95 (require 'crm-custom)
96 (crm-custom-mode 1)
97 ```
9898 Make sure to read and understand the FAQ entry below about the empty
9999 entry at the beginning of the completion list before using this mode,
100100 or using it will likely be very confusing.
116116 called `icomplete-mode` that integrates with standard emacs completion
117117 and adds some ido-like behavior. It is built in to emacs, so no
118118 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 ```
123123 # Frequently asked questions #
124124
125125 ## How does ido-ubiquitous-mode decide when to replace `completing-read`? <br/> Why don't some commands use ido completion? ##
44 ;; Filename: ido-completing-read+.el
55 ;; Author: Ryan Thompson
66 ;; Created: Sat Apr 4 13:41:20 2015 (-0700)
7 ;; Version: 4.7
7 ;; Version: 4.10
88 ;; Package-Requires: ((emacs "24.4") (cl-lib "0.5") (s "0.1") (memoize "1.1"))
99 ;; URL: https://github.com/DarwinAwardWinner/ido-completing-read-plus
1010 ;; Keywords: ido, completion, convenience
7676 ;;
7777 ;;; Code:
7878
79 (defconst ido-completing-read+-version "4.7"
79 (defconst ido-completing-read+-version "4.10"
8080 "Currently running version of ido-completing-read+.
8181
8282 Note that when you update ido-completing-read+, this variable may
181181 This allows ido-cr+ to update the set of completion candidates
182182 dynamically.")
183183
184 (defvar ido-cr+-last-dynamic-update-text nil
185 "The value of `ido-text' last time a dynamic update occurred.")
186
184187 (defvar ido-cr+-dynamic-update-idle-time 0.25
185188 "Time to wait before updating dynamic completion list.")
186189
205208
206209 These are used for falling back to `completing-read-default'.")
207210
208 (defvar ido-cr+-all-completions-memoized nil
211 (defvar ido-cr+-all-completions-memoized 'all-completions
209212 "Memoized version of `all-completions'.
210213
211214 During completion with dynamic collection, this variable is set
212215 to a memoized copy of `all-completions'.")
213216
214 (defvar ido-cr+-all-prefix-completions-memoized nil
217 (defvar ido-cr+-all-prefix-completions-memoized 'ido-cr+-all-prefix-completions
215218 "Memoized version of `ido-cr+-all-prefix-completions'.
216219
217220 During completion with dynamic collection, this variable is set
510513 (when (and (not ido-cr+-assume-static-collection)
511514 (functionp collection))
512515 collection))
516 (ido-cr+-last-dynamic-update-text nil)
513517 ;; Only memoize if the collection is dynamic.
514518 (ido-cr+-all-prefix-completions-memoized
515519 (if ido-cr+-dynamic-collection
827831 (cl-loop
828832 for i from 0 upto (length string)
829833 append (funcall
830 (or ido-cr+-all-completions-memoized
831 'all-completions)
834 ido-cr+-all-completions-memoized
832835 (s-left i string)
833836 collection
834837 predicate)
835838 into completion-list
836839 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.
839843 (t
840844 (all-completions "" collection predicate))))
841845
872876 (nreverse filtered-collection)
873877 filtered-collection)))
874878
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
875899 (defun ido-cr+-update-dynamic-collection ()
876900 "Update the set of completions for a dynamic collection.
877901
878902 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))))))
948984 ;; Always cancel an active timer when this function is called.
949985 (when ido-cr+-dynamic-update-timer
950986 (cancel-timer ido-cr+-dynamic-update-timer)
958994 (when ido-cr+-dynamic-update-timer
959995 (cancel-timer ido-cr+-dynamic-update-timer)
960996 (setq ido-cr+-dynamic-update-timer nil))
997 (cl-assert (not (ido-cr+-cyclicp ido-cur-list)))
961998 (if (<= (length ido-matches) 1)
962999 ;; If we've narrowed it down to zero or one matches, update
9631000 ;; immediately.
9861023 (apply oldfun args))
9871024 ;; Update `ido-eoinput'
9881025 (setq ido-eoinput (point-max))
1026 ;; Clear this var to force an update
1027 (setq ido-cr+-last-dynamic-update-text nil)
9891028 ;; Now do update
9901029 (ido-cr+-update-dynamic-collection))
9911030 ;; After maybe updating the dynamic collection, if there's still
33
44 ;; Author: Ryan C. Thompson
55 ;; URL: https://github.com/DarwinAwardWinner/ido-ubiquitous
6 ;; Version: 4.7
6 ;; Version: 4.10
77 ;; Created: 2011-09-01
88 ;; Keywords: convenience, completion, ido
99 ;; 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"))
1111 ;; Filename: ido-ubiquitous.el
1212
1313 ;; This file is NOT part of GNU Emacs.
3838 ;;
3939 ;;; Code:
4040
41 (defconst ido-ubiquitous-version "4.7"
41 (defconst ido-ubiquitous-version "4.10"
4242 "Currently running version of ido-ubiquitous.
4343
4444 Note that when you update ido-ubiquitous, this variable may not
0 (require 'undercover)
1 (undercover "*.el"
2 (:exclude "test-*.el"))
00 ;;; -*- lexical-binding: t -*-
11
2 (require 'undercover)
3 (undercover "*.el"
4 (:exclude "test-*.el"))
5
62 (require 'ido)
7 (require 'flx-ido)
83 (require 'minibuf-eldef)
94 (require 'ido-completing-read+)
105 (require 'buttercup)
112107 (when (eq (car vars) 'quote)
113108 (setq vars (eval vars)))
114109 `(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)))))
115121
116122 (describe "Within the `ido-completing-read+' package"
117123
132138 ido-confirm-unique-completion
133139 ido-enable-flex-matching
134140 ido-enable-dot-prefix
135 flx-ido-mode
136141 (minibuffer-electric-default-mode t)))
137142
138143 ;; Suppress all messages during tests
564569 (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
565570 (ido-completing-read+
566571 "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"))))
670573
671574 (describe "with unusual inputs"
672575 (it "should accept a COLLECTION of symbols"
1010913 (expect
1011914 (with-simulated-input "RET"
1012915 (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"))))
1018973
1019974 ;;; 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