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

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