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

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