test-ido-completing-read+.el 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. ;;; -*- lexical-binding: t -*-
  2. (require 'undercover)
  3. (undercover "*.el"
  4. (:exclude "test-*.el"))
  5. (require 'ido)
  6. (require 'minibuf-eldef)
  7. (require 'ido-completing-read+)
  8. (require 'buttercup)
  9. (require 'cl-lib)
  10. (require 'with-simulated-input)
  11. (defun collection-as-function (collection)
  12. "Return a function equivalent to COLLECTION.
  13. The returned function will work equivalently to COLLECTION when
  14. passed to `all-completions' and `try-completion'."
  15. (completion-table-dynamic (lambda (string) (all-completions string collection))))
  16. (defun shadow-var (var &optional temp-value)
  17. "Shadow the value of VAR.
  18. This will push the current value of VAR to VAR's
  19. `shadowed-values' property, and then set it to TEMP-VALUE. To
  20. reverse this process, call `unshadow-var' on VAR. Vars can
  21. be shadowed recursively, and must be unshadowed once for each
  22. shadowing in order to restore the original value. You can think
  23. of shadowing as dynamic binding with `let', but with manual
  24. control over when bindings start and end.
  25. If VAR is a Custom variable (see `custom-variable-p'), it will be
  26. set using `customize-set-variable', and if TEMP-VALUE is nil it
  27. will be replaces with VAR's standard value.
  28. Other variables will be set with `set-default', and a TEMP-VALUE
  29. of nil will not be treated specially.
  30. `shadow-var' only works on variables declared as special (i.e.
  31. using `defvar' or similar). It will not work on lexically bound
  32. variables."
  33. (unless (special-variable-p var)
  34. (error "Cannot shadow lexical var `%s'" var))
  35. (let* ((use-custom (custom-variable-p var))
  36. (setter (if use-custom 'customize-set-variable 'set-default))
  37. (temp-value (or temp-value
  38. (and use-custom
  39. (eval (car (get var 'standard-value)))))))
  40. ;; Push the current value on the stack
  41. (push (symbol-value var) (get var 'shadowed-values))
  42. (funcall setter var temp-value)))
  43. (defun var-shadowed-p (var)
  44. "Return non-nil if VAR is shadowed by `shadow-var'."
  45. ;; We don't actually want to return that list if it's non-nil.
  46. (and (get var 'shadowed-values) t))
  47. (defun unshadow-var (var)
  48. "Reverse the last call to `shadow-var' on VAR."
  49. (if (var-shadowed-p var)
  50. (let* ((use-custom (custom-variable-p var))
  51. (setter (if use-custom 'customize-set-variable 'set-default))
  52. (value (pop (get var 'shadowed-values))))
  53. (funcall setter var value))
  54. (error "Var is not shadowed: %s" var)))
  55. (defun fully-unshadow-var (var)
  56. "Reverse *all* calls to `shadow-var' on VAR."
  57. (when (var-shadowed-p var)
  58. (let* ((use-custom (custom-variable-p var))
  59. (setter (if use-custom 'customize-set-variable 'set-default))
  60. (value (car (last (get var 'shadowed-values)))))
  61. (put var 'shadowed-values nil)
  62. (funcall setter var value))))
  63. (defun fully-unshadow-all-vars (&optional vars)
  64. "Reverse *all* calls to `shadow-var' on VARS.
  65. If VARS is nil, unshadow *all* variables."
  66. (if vars
  67. (mapc #'fully-unshadow-var vars)
  68. (mapatoms #'fully-unshadow-var))
  69. nil)
  70. (defmacro shadow-vars (varlist)
  71. "Shadow a list of vars with new values.
  72. VARLIST describes the variables to be shadowed with the same
  73. syntax as `let'.
  74. See `shadow-var'."
  75. (declare (indent 0))
  76. (cl-loop
  77. with var = nil
  78. with value = nil
  79. for binding in varlist
  80. if (symbolp binding)
  81. do (setq var binding
  82. value nil)
  83. else
  84. do (setq var (car binding)
  85. value (cadr binding))
  86. collect `(shadow-var ',var ,value) into exprs
  87. finally return `(progn ,@exprs)))
  88. (defmacro unshadow-vars (vars)
  89. "Un-shadow a list of VARS.
  90. This is a macro for consistency with `shadow-vars', but it will
  91. also accept a quoted list for the sake of convenience."
  92. (declare (indent 0))
  93. (when (eq (car vars) 'quote)
  94. (setq vars (eval vars)))
  95. `(mapc #'unshadow-var ',vars))
  96. (defmacro with-temp-info-buffer (&rest body)
  97. "Create a temporary info buffer and exeluate BODY forms there."
  98. (declare (indent 0))
  99. `(let ((temp-bufname (generate-new-buffer-name " *temp-info*")))
  100. (unwind-protect
  101. (save-excursion
  102. (info nil (generate-new-buffer-name " *temp-info*"))
  103. ,@body)
  104. (when (get-buffer temp-bufname)
  105. (kill-buffer temp-bufname)))))
  106. (describe "Within the `ido-completing-read+' package"
  107. ;; Reset all of these variables to their standard values before each
  108. ;; test, saving the previous values for later restoration.
  109. (before-each
  110. (shadow-vars
  111. ((ido-mode t)
  112. (ido-ubiquitous-mode t)
  113. (ido-cr+-debug-mode t)
  114. ido-cr+-auto-update-blacklist
  115. ido-cr+-fallback-function
  116. ido-cr+-max-items
  117. ido-cr+-function-blacklist
  118. ido-cr+-function-whitelist
  119. ido-cr+-nil-def-alternate-behavior-list
  120. ido-cr+-replace-completely
  121. ido-confirm-unique-completion
  122. ido-enable-flex-matching
  123. ido-enable-dot-prefix
  124. (minibuffer-electric-default-mode t)))
  125. ;; Suppress all messages during tests
  126. (spy-on 'message))
  127. ;; Restore the saved values after each test
  128. (after-each
  129. (fully-unshadow-all-vars))
  130. (describe "the `ido-completing-read+' function"
  131. (it "should complete with a matching item on RET"
  132. (expect
  133. (with-simulated-input "g RET"
  134. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  135. :to-equal "green"))
  136. (it "should complete with the first match when multiple matches are available"
  137. (expect
  138. (with-simulated-input "b RET"
  139. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
  140. :to-equal "brown"))
  141. (it "should allow <left> and <right> to cycle completions, with wrap-around"
  142. (expect
  143. (with-simulated-input "b <right> <right> <right> <right> <left> RET"
  144. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
  145. :to-equal
  146. "blue"))
  147. (it "should return \"\" when RET or C-j is pressed on an empty input even when REQUIRE-MATCH is non-nil"
  148. ;; No REQUIRE-MATCH
  149. (expect
  150. (with-simulated-input "RET"
  151. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  152. :to-equal "blue")
  153. (expect
  154. (with-simulated-input "C-j"
  155. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  156. :to-equal "")
  157. ;; Again, with REQUIRE-MATCH
  158. (expect
  159. (with-simulated-input "RET"
  160. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
  161. :to-equal "")
  162. (expect
  163. (with-simulated-input "C-j"
  164. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
  165. :to-equal ""))
  166. (it "should accept all the same forms of DEF as `completing-read-default'"
  167. ;; DEF in COLLECTION
  168. (expect
  169. (with-simulated-input "RET"
  170. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "green"))
  171. :to-equal "green")
  172. ;; Same, with REQUIRE-MATCH
  173. (expect
  174. (with-simulated-input "RET"
  175. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "green"))
  176. :to-equal "green")
  177. ;; DEF not in COLLECTION
  178. (expect
  179. (with-simulated-input "RET"
  180. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "brown"))
  181. :to-equal "brown")
  182. ;; Same, with REQUIRE-MATCH
  183. (expect
  184. (with-simulated-input "RET"
  185. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "brown"))
  186. :to-equal "brown")
  187. ;; List DEF, partially in COLLECTION
  188. (expect
  189. (with-simulated-input "RET"
  190. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil '("brown" "green")))
  191. :to-equal "brown"))
  192. (it "should work with INITIAL-INPUT"
  193. (expect
  194. (with-simulated-input "RET"
  195. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr"))
  196. :to-equal "green"))
  197. (it "should properly handle a cons INITIAL-INPUT"
  198. (expect
  199. (with-simulated-input "ee RET"
  200. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil (cons "gr" 2)))
  201. :to-equal "green"))
  202. (it "should properly handle both INITIAL-INPUT and DEF at the same time"
  203. (expect
  204. (with-simulated-input "RET"
  205. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
  206. :to-equal "green")
  207. (expect
  208. (with-simulated-input "DEL DEL RET"
  209. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
  210. :to-equal "blue"))
  211. (it "should work when COLLECTION is a function"
  212. (expect
  213. (with-simulated-input "g RET"
  214. (ido-completing-read+ "Prompt: " (collection-as-function '("blue" "yellow" "green"))))
  215. :to-equal "green"))
  216. (it "should fall back when COLLECTION is empty"
  217. (spy-on 'ido-completing-read :and-call-through)
  218. (expect
  219. (with-simulated-input "g RET"
  220. (ido-completing-read+ "Prompt: " nil))
  221. :to-equal "g")
  222. (expect 'ido-completing-read :not :to-have-been-called))
  223. (it "should replace `ido-completing-read' when `ido-cr+-replace-completely' is non-nil"
  224. (customize-set-variable 'ido-cr+-replace-completely t)
  225. (spy-on 'ido-completing-read+ :and-call-through)
  226. (expect
  227. (with-simulated-input "g RET"
  228. (ido-completing-read "Prompt: " '("blue" "yellow" "green")))
  229. :to-equal "green")
  230. (expect 'ido-completing-read+ :to-have-been-called))
  231. (describe "when `ido-cr+-max-items' is set"
  232. (it "should not trigger a fallback for small collections"
  233. (expect
  234. (with-simulated-input "g RET"
  235. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  236. :to-equal "green"))
  237. (it "should trigger a fallback for large collections"
  238. (expect
  239. ;; With max-items negative, all collections are considered "too
  240. ;; large"
  241. (let ((ido-cr+-max-items -1))
  242. (with-simulated-input "g RET"
  243. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green"))))
  244. :to-equal "g")))
  245. (describe "when REQUIRE-MATCH is non-nil"
  246. (it "should still allow exiting with an empty string if DEF is nil"
  247. (expect
  248. (with-simulated-input "C-j"
  249. (ido-completing-read+
  250. "Prompt: "
  251. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  252. :to-equal ""))
  253. ;; "C-j" should NOT be allowed to return an empty string if
  254. ;; require-match and default are both non-nil.
  255. (it "should not alow exiting with an empty string if DEF is non-nil"
  256. (expect
  257. (with-simulated-input "C-j"
  258. (ido-completing-read+
  259. "Prompt: "
  260. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t nil nil "yellow"))
  261. :to-throw))
  262. (it "shouldn't allow C-j to select an ambiguous match"
  263. (expect
  264. (with-simulated-input "b C-j C-j C-j"
  265. (ido-completing-read+
  266. "Prompt: "
  267. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  268. :to-throw)
  269. ;; First press of C-j should complete to "blue" after the
  270. ;; first b, but then get stuck on the choice for the second b.
  271. (expect
  272. (with-simulated-input "b C-j b C-j C-j C-j"
  273. (ido-completing-read+
  274. "Prompt: "
  275. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  276. :to-throw))
  277. (it "should allow exiting with an unambiguous match"
  278. (expect
  279. (with-simulated-input "b C-j b C-j e C-j C-j"
  280. (ido-completing-read+
  281. "Prompt: "
  282. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  283. :to-equal "blueberry")
  284. ;; The "C-j" should complete to "bluegrass" and return, because
  285. ;; `ido-confirm-unique-completion is nil.
  286. (expect
  287. (with-simulated-input "b l u e g C-j"
  288. (ido-completing-read+
  289. "Prompt: "
  290. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  291. :to-equal "bluegrass"))
  292. (it "should require an extra C-j to exit when `ido-confirm-unique-completion' is non-nil"
  293. (setq ido-confirm-unique-completion t)
  294. ;; Now the first "C-j" should complete to "bluegrass" but should
  295. ;; not return.
  296. (expect
  297. (with-simulated-input "b l u e g C-j"
  298. (ido-completing-read+
  299. "Prompt: "
  300. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  301. :to-throw)
  302. ;; The first "C-j" should complete to "bluegrass", and the second
  303. ;; should return.
  304. (expect
  305. (with-simulated-input "b l u e g C-j C-j"
  306. (ido-completing-read+
  307. "Prompt: "
  308. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  309. :to-equal "bluegrass"))
  310. ;; Finally, a test for the expected wrong behavior without
  311. ;; ido-cr+. If ido.el ever fixes this bug, it will cause this test
  312. ;; to fail as a signal that the workaround can be phased out.
  313. (it "should return a non-match when ordinary `ido-completing-read' is used"
  314. (expect
  315. (with-simulated-input "b C-j"
  316. (ido-completing-read
  317. "Prompt: "
  318. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  319. :to-equal "b")))
  320. (describe "when INHERIT-INPUT-METHOD is non-nil"
  321. (before-each
  322. (spy-on 'ido-completing-read :and-call-through))
  323. (it "should not fall back if `current-input-method' is nil"
  324. (expect
  325. (let ((current-input-method nil))
  326. (with-simulated-input "g RET"
  327. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  328. :to-equal "green"))
  329. (expect 'ido-completing-read :to-have-been-called))
  330. (it "should fall back if `current-input-method' is non-nil"
  331. (expect
  332. (let ((current-input-method 'ucs))
  333. (with-simulated-input "g RET"
  334. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  335. :to-equal "green"))
  336. (expect 'ido-completing-read :not :to-have-been-called)))
  337. (describe "with manual fallback shortcuts"
  338. (it "should not fall back when C-b or C-f is used in the middle of the input"
  339. (expect
  340. ;; C-b/f not at beginning/end of input should not fall back
  341. (with-simulated-input "g C-b C-f RET"
  342. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  343. :to-equal "green"))
  344. (it "should fall back on C-f at end of input"
  345. (expect
  346. ;; C-f at end of input should fall back
  347. (with-simulated-input "g C-f RET"
  348. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  349. :to-equal "g"))
  350. (it "should not fall back from repeated C-b that hits the start of input"
  351. (expect
  352. ;; Repeated C-b should not fall back
  353. (with-simulated-input "g C-b C-b C-b C-b RET"
  354. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  355. :to-equal "green"))
  356. (it "should fall back on C-b at beginning of input (if previous action was not C-b)"
  357. (expect
  358. ;; C-b at beginning of line should fall back (if previous action
  359. ;; was not also C-b)
  360. (with-simulated-input "g C-b x DEL C-b RET"
  361. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  362. :to-equal "g")))
  363. (describe "with a workaround for an bug with non-nil `ido-enable-dot-prefix'"
  364. ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997
  365. ;; for more information on this bug.
  366. (before-each
  367. (setq ido-enable-dot-prefix t))
  368. (it "should not throw an error when \"\" is in the collection"
  369. (expect
  370. (with-simulated-input "RET"
  371. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  372. :to-equal "")
  373. (expect
  374. (with-simulated-input "a a b RET"
  375. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  376. :to-equal "aab")))
  377. (describe "with dynamic collections"
  378. (before-all
  379. (setq my-dynamic-collection
  380. (completion-table-dynamic
  381. (lambda (text)
  382. (cond
  383. ;; Sub-completions for "hello"
  384. ((s-prefix-p "hello" text)
  385. '("hello" "hello-world" "hello-everyone" "hello-universe"))
  386. ;; Sub-completions for "goodbye"
  387. ((s-prefix-p "goodbye" text)
  388. '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe"))
  389. ;; General completions
  390. (t
  391. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill")))))))
  392. (after-all
  393. (setq my-dynamic-collection nil))
  394. (before-each
  395. (setq ido-enable-flex-matching t
  396. ido-confirm-unique-completion nil)
  397. (spy-on 'ido-cr+-update-dynamic-collection
  398. :and-call-through))
  399. (it "should allow selection of dynamically-added completions"
  400. (expect
  401. (with-simulated-input "hello- RET"
  402. (ido-completing-read+ "Say something: " my-dynamic-collection))
  403. :to-equal "hello-world")
  404. (expect 'ido-cr+-update-dynamic-collection
  405. :to-have-been-called))
  406. (it "should allow ido flex-matching of dynamically-added completions"
  407. (expect
  408. (with-simulated-input "hello-ld RET"
  409. (ido-completing-read+ "Say something: " my-dynamic-collection))
  410. :to-equal
  411. "hello-world")
  412. (expect 'ido-cr+-update-dynamic-collection
  413. :to-have-been-called))
  414. (it "should do a dynamic update when pressing TAB"
  415. (expect
  416. (with-simulated-input "h TAB -ld RET"
  417. (ido-completing-read+ "Say something: " my-dynamic-collection))
  418. :to-equal
  419. "hello-world")
  420. (expect 'ido-cr+-update-dynamic-collection
  421. :to-have-been-called))
  422. (it "should do a dynamic update when idle"
  423. (expect
  424. (with-simulated-input
  425. '("h"
  426. (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
  427. "-ld RET")
  428. (ido-completing-read+ "Say something: " my-dynamic-collection))
  429. :to-equal
  430. "hello-world")
  431. (expect 'ido-cr+-update-dynamic-collection
  432. :to-have-been-called))
  433. (it "should do a dynamic update when there is only one match remaining"
  434. (expect
  435. (with-simulated-input "hell-ld RET"
  436. (ido-completing-read+ "Say something: " my-dynamic-collection))
  437. :to-equal
  438. "hello-world")
  439. (expect 'ido-cr+-update-dynamic-collection
  440. :to-have-been-called))
  441. (it "should not exit with a unique match if new matches are dynamically added"
  442. (expect
  443. (with-simulated-input '("hell TAB -ld RET")
  444. (ido-completing-read+ "Say something: " my-dynamic-collection))
  445. :to-equal
  446. "hello-world")
  447. (expect 'ido-cr+-update-dynamic-collection
  448. :to-have-been-called))
  449. (it "should exit with a match that is still unique after dynamic updating"
  450. (expect
  451. (with-simulated-input '("helic TAB")
  452. (ido-completing-read+ "Say something: " my-dynamic-collection))
  453. :to-equal
  454. "helicopter")
  455. (expect 'ido-cr+-update-dynamic-collection
  456. :to-have-been-called))
  457. (it "should suppress errors raised by dynamic completion updates"
  458. (let ((collection
  459. (completion-table-dynamic
  460. (lambda (text)
  461. (cond
  462. ((equal text "")
  463. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill"))
  464. (t (error "This collection throws an error on a nonempty prefix"))))))
  465. ;; The test framework uses the debugger to catch error
  466. ;; stack traces, but we want to run this code as if it
  467. ;; was not being debugged.
  468. (debug-on-error nil))
  469. (expect
  470. (with-simulated-input '("hell TAB RET")
  471. (ido-completing-read+ "Say something: " collection))
  472. :to-equal
  473. "hello")))
  474. (it "should respect `ido-restrict-to-matches' when doing dynamic updates"
  475. (assume (version<= "25" emacs-version))
  476. (let ((collection
  477. (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg"
  478. "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg"
  479. "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg"
  480. "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh"
  481. "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh"
  482. "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh"
  483. "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii"
  484. "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii"
  485. "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii")))
  486. ;; Test the internal function
  487. (expect
  488. (ido-cr+-apply-restrictions
  489. collection
  490. (list (cons nil "bbb")
  491. (cons nil "eee")))
  492. :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii"))
  493. ;; First verify it without a dynamic collection
  494. (expect
  495. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  496. (ido-completing-read+
  497. "Pick: " collection nil t nil nil (car collection)))
  498. :to-equal "bbb-eee-ggg")
  499. (expect
  500. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  501. (ido-completing-read+
  502. "Pick: " collection nil t nil nil (car collection)))
  503. :to-equal "bbb-eee-ggg")
  504. ;; Now test the same with a dynamic collection
  505. (expect
  506. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  507. (ido-completing-read+
  508. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  509. :to-equal "bbb-eee-ggg")
  510. (expect
  511. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  512. (ido-completing-read+
  513. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  514. :to-equal "bbb-eee-ggg"))))
  515. (describe "with unusual inputs"
  516. (it "should accept a COLLECTION of symbols"
  517. (expect
  518. (with-simulated-input "g RET"
  519. (ido-completing-read+ "Prompt: " '(blue yellow green)))
  520. :to-equal "green"))
  521. (it "should accept a mix of strings and symbols in COLLECTION"
  522. (expect
  523. (with-simulated-input "g RET"
  524. (ido-completing-read+ "Prompt: " '(blue "yellow" green)))
  525. :to-equal "green"))
  526. (it "should accept symbols in DEF"
  527. (expect
  528. (with-simulated-input "RET"
  529. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '(brown "green")))
  530. :to-equal "brown"))
  531. (it "should accept an alist COLLECTION"
  532. (expect
  533. (with-simulated-input "RET"
  534. (ido-completing-read+
  535. "Prompt: "
  536. '(("blue" . blue-value)
  537. ("yellow" . yellow-value)
  538. (green . green-value))
  539. nil nil nil nil "green"))
  540. :to-equal "green"))
  541. (it "should accept a hash table COLLECTION"
  542. (expect
  543. (with-simulated-input "RET"
  544. (let ((collection (make-hash-table)))
  545. (puthash "blue" 'blue-value collection)
  546. (puthash "yellow" 'yellow-value collection)
  547. (puthash 'green 'green-value collection)
  548. (ido-completing-read+ "Prompt: " collection nil nil nil nil "green")))
  549. :to-equal "green"))
  550. (it "should accept an obarray COLLECTION"
  551. (expect
  552. (with-simulated-input "forward-char RET"
  553. (ido-completing-read+ "Prompt: " obarray #'commandp
  554. t nil nil "backward-char"))
  555. :to-equal "forward-char"))))
  556. (describe "ido-ubiquitous-mode"
  557. ;; Set up a test command that calls `completing-read'
  558. (before-all
  559. (setf (symbol-function 'test-command)
  560. (lambda ()
  561. (interactive)
  562. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  563. ;; Delete the test command
  564. (after-all
  565. (setf (symbol-function 'test-command) nil))
  566. ;; Verify that the mode can be activated
  567. (it "should enable itself properly"
  568. (expect
  569. (progn
  570. (ido-ubiquitous-mode 1)
  571. (with-simulated-input "g RET"
  572. (command-execute 'test-command)))
  573. :to-equal "green"))
  574. (it "should disable itself properly"
  575. (expect
  576. (progn
  577. (ido-ubiquitous-mode 0)
  578. (with-simulated-input "g RET"
  579. (command-execute 'test-command)))
  580. :to-equal "g"))
  581. (describe "with `ido-cr+-function-blacklist'"
  582. (before-all
  583. (setf (symbol-function 'blacklisted-command)
  584. (lambda (arg)
  585. (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
  586. arg)
  587. (symbol-function 'blacklisted-function)
  588. (lambda ()
  589. (completing-read "Prompt: " '("blue" "yellow" "green")))
  590. (symbol-function 'cmd-that-calls-blacklisted-function)
  591. (lambda ()
  592. (interactive)
  593. (funcall 'blacklisted-function))
  594. (symbol-function 'blacklisted-collection)
  595. (collection-as-function '("blue" "yellow" "green"))))
  596. (after-all
  597. (setf (symbol-function 'blacklisted-command) nil
  598. (symbol-function 'blacklisted-function) nil
  599. (symbol-function 'cmd-that-calls-blacklisted-function) nil
  600. (symbol-function 'blacklisted-collection) nil))
  601. ;; First verify that they work normally before blacklisting them
  602. (describe "when the specified functions are not blacklisted"
  603. (it "should not affect a non-blacklisted command"
  604. (expect
  605. (with-simulated-input "g RET"
  606. (call-interactively 'blacklisted-command))
  607. :to-equal "green"))
  608. (it "should not affect a non-blacklisted function"
  609. (expect
  610. (with-simulated-input "g RET"
  611. (call-interactively 'cmd-that-calls-blacklisted-function))
  612. :to-equal "green"))
  613. (it "should not affect a non-blacklisted collection"
  614. (expect
  615. (with-simulated-input "g RET"
  616. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  617. :to-equal "green")))
  618. (describe "when the specified functions are blacklisted"
  619. (before-each
  620. (setq ido-cr+-function-blacklist
  621. (append '(blacklisted-command
  622. blacklisted-function
  623. blacklisted-collection)
  624. ido-cr+-function-blacklist)))
  625. (it "should prevent ido in a blacklisted command"
  626. (expect
  627. (with-simulated-input "g RET"
  628. (call-interactively 'blacklisted-command))
  629. :to-equal "g"))
  630. (it "should prevent ido in a blacklisted function"
  631. (expect
  632. (with-simulated-input "g RET"
  633. (call-interactively 'cmd-that-calls-blacklisted-function))
  634. :to-equal "g"))
  635. (it "should prevent ido with a blacklisted collection"
  636. (expect
  637. (with-simulated-input "g RET"
  638. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  639. :to-equal "g")))
  640. (describe "when updating ido-cr+"
  641. (before-each
  642. (spy-on 'ido-cr+-update-blacklist :and-call-through))
  643. (it "should update the blacklist when `ido-cr+-auto-update-blacklist' is t"
  644. (assume ido-cr+-function-blacklist)
  645. (let ((orig-blacklist ido-cr+-function-blacklist))
  646. (customize-set-variable 'ido-cr+-auto-update-blacklist t)
  647. (customize-set-variable 'ido-cr+-function-blacklist nil)
  648. (ido-cr+-maybe-update-blacklist)
  649. (expect 'ido-cr+-update-blacklist :to-have-been-called)
  650. (expect ido-cr+-function-blacklist :to-have-same-items-as orig-blacklist)))
  651. (it "should not update the blacklist when `ido-cr+-auto-update-blacklist' is nil"
  652. (assume ido-cr+-function-blacklist)
  653. (let ((orig-blacklist ido-cr+-function-blacklist))
  654. (customize-set-variable 'ido-cr+-auto-update-blacklist nil)
  655. (customize-set-variable 'ido-cr+-function-blacklist nil)
  656. (ido-cr+-maybe-update-blacklist)
  657. (expect 'ido-cr+-update-blacklist :not :to-have-been-called)
  658. (expect ido-cr+-function-blacklist :to-have-same-items-as nil)))
  659. (it "should notify about blacklist updates when `ido-cr+-auto-update-blacklist' is `notify'"
  660. (assume ido-cr+-function-blacklist)
  661. (spy-on 'display-warning)
  662. (let ((orig-blacklist ido-cr+-function-blacklist))
  663. (customize-set-variable 'ido-cr+-auto-update-blacklist 'notify)
  664. (customize-set-variable 'ido-cr+-function-blacklist nil)
  665. (ido-cr+-maybe-update-blacklist)
  666. (expect 'ido-cr+-update-blacklist :not :to-have-been-called)
  667. (expect 'display-warning :to-have-been-called)
  668. (expect ido-cr+-function-blacklist :to-have-same-items-as nil)))))
  669. (describe "with `ido-cr+-function-whitelist'"
  670. (before-all
  671. (setf (symbol-function 'whitelisted-command)
  672. (lambda (arg)
  673. (interactive
  674. (list
  675. (completing-read "Prompt: " '("blue" "yellow" "green"))))
  676. arg)
  677. (symbol-function 'whitelisted-function)
  678. (lambda ()
  679. (completing-read "Prompt: " '("blue" "yellow" "green")))
  680. (symbol-function 'cmd-that-calls-whitelisted-function)
  681. (lambda ()
  682. (interactive)
  683. (funcall 'whitelisted-function))
  684. (symbol-function 'whitelisted-collection)
  685. (lambda (string pred action)
  686. (complete-with-action action '("blue" "yellow" "green") string pred))))
  687. (after-all
  688. (setf (symbol-function 'whitelisted-command) nil
  689. (symbol-function 'whitelisted-function) nil
  690. (symbol-function 'cmd-that-calls-whitelisted-function) nil
  691. (symbol-function 'whitelisted-collection) nil))
  692. (describe "when the whitelist is inactive (i.e. everything is whitelisted)"
  693. (before-each
  694. (setq ido-cr+-function-whitelist nil))
  695. (it "should enable ido in a command"
  696. (expect
  697. (with-simulated-input "g RET"
  698. (call-interactively 'whitelisted-command))
  699. :to-equal "green"))
  700. (it "should enable ido in a function"
  701. (expect
  702. (with-simulated-input "g RET"
  703. (call-interactively 'cmd-that-calls-whitelisted-function))
  704. :to-equal "green"))
  705. (it "should enable ido for a collection"
  706. (expect
  707. (with-simulated-input "g RET"
  708. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  709. :to-equal "green")))
  710. (describe "when the specified functions are whitelisted"
  711. (before-each
  712. (setq ido-cr+-function-whitelist
  713. (append '(whitelisted-command
  714. whitelisted-function
  715. whitelisted-collection)
  716. ido-cr+-function-whitelist)))
  717. (it "should enable ido in a whitelisted command"
  718. (expect
  719. (with-simulated-input "g RET"
  720. (call-interactively 'whitelisted-command))
  721. :to-equal "green"))
  722. (it "should enable ido in a whitelisted function"
  723. (expect
  724. (with-simulated-input "g RET"
  725. (call-interactively 'cmd-that-calls-whitelisted-function))
  726. :to-equal "green"))
  727. (it "should enable ido for a whitelisted collection"
  728. (expect
  729. (with-simulated-input "g RET"
  730. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  731. :to-equal "green")))
  732. (describe "when the whitelist is active but empty (i.e. nothing whitelisted)"
  733. (before-each
  734. (setq ido-cr+-function-whitelist (list nil)))
  735. (it "should prevent ido in a command"
  736. (expect
  737. (with-simulated-input "g RET"
  738. (call-interactively 'whitelisted-command))
  739. :to-equal "g"))
  740. (it "should prevent ido in a function"
  741. (expect
  742. (with-simulated-input "g RET"
  743. (call-interactively 'cmd-that-calls-whitelisted-function))
  744. :to-equal "g"))
  745. (it "should prevent ido for a collection"
  746. (expect
  747. (with-simulated-input "g RET"
  748. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  749. :to-equal "g"))))
  750. (describe "with `ido-cr+-nil-def-alternate-behavior-list'"
  751. (before-all
  752. (setf (symbol-function 'def-nil-command)
  753. (lambda (arg)
  754. (interactive
  755. (list
  756. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))
  757. arg)
  758. (symbol-function 'def-nil-function)
  759. (lambda ()
  760. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))
  761. (symbol-function 'cmd-that-calls-def-nil-function)
  762. (lambda ()
  763. (interactive)
  764. (funcall 'def-nil-function))
  765. (symbol-function 'def-nil-collection)
  766. (lambda (string pred action)
  767. (complete-with-action action '("blue" "yellow" "green") string pred))))
  768. (after-all
  769. (setf (symbol-function 'def-nil-command) nil
  770. (symbol-function 'def-nil-function) nil
  771. (symbol-function 'cmd-that-calls-def-nil-function) nil
  772. (symbol-function 'def-nil-collection) nil))
  773. (describe "when the specified functions are not in the list"
  774. (before-each
  775. (setq ido-cr+-nil-def-alternate-behavior-list nil))
  776. (it "should use empty string default in a command"
  777. (expect
  778. (with-simulated-input "RET"
  779. (call-interactively 'def-nil-command))
  780. :to-equal ""))
  781. (it "should use empty string default in a function"
  782. (expect
  783. (with-simulated-input "RET"
  784. (call-interactively 'cmd-that-calls-def-nil-function))
  785. :to-equal ""))
  786. (it "should use empty string default for a collection"
  787. (expect
  788. (with-simulated-input "RET"
  789. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  790. :to-equal "")))
  791. (describe "when the specified functions are in the list"
  792. (before-each
  793. (setq ido-cr+-nil-def-alternate-behavior-list
  794. (append '(def-nil-command
  795. def-nil-function
  796. def-nil-collection)
  797. ido-cr+-nil-def-alternate-behavior-list)))
  798. (it "should not use empty string default in a command"
  799. (expect
  800. (with-simulated-input "RET"
  801. (call-interactively 'def-nil-command))
  802. :to-equal "blue"))
  803. (it "should not use empty string default in a function"
  804. (expect
  805. (with-simulated-input "RET"
  806. (call-interactively 'cmd-that-calls-def-nil-function))
  807. :to-equal "blue"))
  808. (it "should not use empty string default for a collection"
  809. (expect
  810. (with-simulated-input "RET"
  811. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  812. :to-equal "blue"))))
  813. ;; Test is currently disabled pending additional information
  814. (xit "should not hang or error when deleting characters in `org-refile' (issue #152)"
  815. (expect
  816. (progn
  817. (ido-ubiquitous-mode 1)
  818. (save-excursion
  819. (with-temp-buffer
  820. (org-mode)
  821. (insert (s-trim "
  822. * Heading 1
  823. ** Subheading 1.1
  824. ** Subheading 1.2
  825. ** Subheading 1.3
  826. * Heading 2
  827. * Heading 3
  828. "))
  829. (goto-char (point-max))
  830. ;; TODO Figure out what else needs to be set up to call
  831. ;; `org-refile'
  832. (with-simulated-input
  833. "Heading DEL DEL DEL DEL DEL RET"
  834. (command-execute 'org-refile)))))
  835. :not :to-throw)))
  836. (describe "regressions should not occur for"
  837. (it "issue #151: should not hang or error when cycling matches in `Info-menu'"
  838. (expect
  839. (progn
  840. (ido-ubiquitous-mode 1)
  841. (with-temp-info-buffer
  842. (with-simulated-input
  843. '("emacs"
  844. (ido-next-match)
  845. (wsi-simulate-idle-time 5)
  846. (ido-next-match)
  847. (wsi-simulate-idle-time 5)
  848. (ido-next-match)
  849. (wsi-simulate-idle-time 5)
  850. (ido-next-match)
  851. (wsi-simulate-idle-time 5)
  852. "RET")
  853. (command-execute 'Info-menu))))
  854. :not :to-throw))
  855. (it "issue #153: should preserve the selected item when doing a deferred dynamic update"
  856. (expect
  857. (with-simulated-input
  858. '("Emacs"
  859. (ido-next-match)
  860. (wsi-simulate-idle-time 5)
  861. "RET")
  862. (ido-completing-read+
  863. "Choose: "
  864. (collection-as-function '("Emacs" "Emacs A" "Emacs B" "Emacs C"))))
  865. :to-equal "Emacs A"))))
  866. ;;; test-ido-completing-read+.el ends here