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

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