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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  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 alow 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. (expect
  290. (with-simulated-input "b C-j C-j C-j"
  291. (ido-completing-read+
  292. "Prompt: "
  293. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  294. :to-throw)
  295. ;; First press of C-j should complete to "blue" after the
  296. ;; first b, but then get stuck on the choice for the second b.
  297. (expect
  298. (with-simulated-input "b C-j b C-j C-j C-j"
  299. (ido-completing-read+
  300. "Prompt: "
  301. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  302. :to-throw))
  303. (it "should allow exiting with an unambiguous match"
  304. (expect
  305. (with-simulated-input "b C-j b C-j e C-j C-j"
  306. (ido-completing-read+
  307. "Prompt: "
  308. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  309. :to-equal "blueberry")
  310. ;; The "C-j" should complete to "bluegrass" and return, because
  311. ;; `ido-confirm-unique-completion is nil.
  312. (expect
  313. (with-simulated-input "b l u e g C-j"
  314. (ido-completing-read+
  315. "Prompt: "
  316. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  317. :to-equal "bluegrass"))
  318. (it "should require an extra C-j to exit when `ido-confirm-unique-completion' is non-nil"
  319. (setq ido-confirm-unique-completion t)
  320. ;; Now the first "C-j" should complete to "bluegrass" but should
  321. ;; not return.
  322. (expect
  323. (with-simulated-input "b l u e g C-j"
  324. (ido-completing-read+
  325. "Prompt: "
  326. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  327. :to-throw)
  328. ;; The first "C-j" should complete to "bluegrass", and the second
  329. ;; should return.
  330. (expect
  331. (with-simulated-input "b l u e g C-j C-j"
  332. (ido-completing-read+
  333. "Prompt: "
  334. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  335. :to-equal "bluegrass"))
  336. ;; Finally, a test for the expected wrong behavior without
  337. ;; ido-cr+. If ido.el ever fixes this bug, it will cause this test
  338. ;; to fail as a signal that the workaround can be phased out.
  339. (it "should return a non-match when ordinary `ido-completing-read' is used"
  340. (expect
  341. (with-simulated-input "b C-j"
  342. (ido-completing-read
  343. "Prompt: "
  344. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  345. :to-equal "b")))
  346. (describe "when INHERIT-INPUT-METHOD is non-nil"
  347. (before-each
  348. (spy-on 'ido-completing-read :and-call-through))
  349. (it "should not fall back if `current-input-method' is nil"
  350. (expect
  351. (let ((current-input-method nil))
  352. (with-simulated-input "g RET"
  353. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  354. :to-equal "green"))
  355. (expect 'ido-completing-read :to-have-been-called))
  356. (it "should fall back if `current-input-method' is non-nil"
  357. (expect
  358. (let ((current-input-method 'ucs))
  359. (with-simulated-input "g RET"
  360. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  361. :to-equal "green"))
  362. (expect 'ido-completing-read :not :to-have-been-called)))
  363. (describe "with manual fallback shortcuts"
  364. (it "should not fall back when C-b or C-f is used in the middle of the input"
  365. (expect
  366. ;; C-b/f not at beginning/end of input should not fall back
  367. (with-simulated-input "g C-b C-f RET"
  368. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  369. :to-equal "green"))
  370. (it "should fall back on C-f at end of input"
  371. (expect
  372. ;; C-f at end of input should fall back
  373. (with-simulated-input "g C-f RET"
  374. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  375. :to-equal "g"))
  376. (it "should not fall back from repeated C-b that hits the start of input"
  377. (expect
  378. ;; Repeated C-b should not fall back
  379. (with-simulated-input "g C-b C-b C-b C-b RET"
  380. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  381. :to-equal "green"))
  382. (it "should fall back on C-b at beginning of input (if previous action was not C-b)"
  383. (expect
  384. ;; C-b at beginning of line should fall back (if previous action
  385. ;; was not also C-b)
  386. (with-simulated-input "g C-b x DEL C-b RET"
  387. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  388. :to-equal "g")))
  389. (describe "with a workaround for an bug with non-nil `ido-enable-dot-prefix'"
  390. ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997
  391. ;; for more information on this bug.
  392. (before-each
  393. (setq ido-enable-dot-prefix t))
  394. (it "should not throw an error when \"\" is in the collection"
  395. (expect
  396. (with-simulated-input "RET"
  397. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  398. :to-equal "")
  399. (expect
  400. (with-simulated-input "a a b RET"
  401. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  402. :to-equal "aab")))
  403. (describe "with dynamic collections"
  404. (before-all
  405. (setq my-dynamic-collection
  406. (completion-table-dynamic
  407. (lambda (text)
  408. (cond
  409. ;; Sub-completions for "hello"
  410. ((s-prefix-p "hello" text)
  411. '("hello" "hello-world" "hello-everyone" "hello-universe"))
  412. ;; Sub-completions for "goodbye"
  413. ((s-prefix-p "goodbye" text)
  414. '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe"))
  415. ;; General completions
  416. (t
  417. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill")))))))
  418. (after-all
  419. (setq my-dynamic-collection nil))
  420. (before-each
  421. (setq ido-enable-flex-matching t
  422. ido-confirm-unique-completion nil)
  423. (spy-on 'ido-cr+-update-dynamic-collection
  424. :and-call-through))
  425. (it "should allow selection of dynamically-added completions"
  426. (expect
  427. (with-simulated-input "hello- RET"
  428. (ido-completing-read+ "Say something: " my-dynamic-collection))
  429. :to-equal "hello-world")
  430. (expect 'ido-cr+-update-dynamic-collection
  431. :to-have-been-called))
  432. (it "should allow ido flex-matching of dynamically-added completions"
  433. (expect
  434. (with-simulated-input "hello-ld RET"
  435. (ido-completing-read+ "Say something: " my-dynamic-collection))
  436. :to-equal
  437. "hello-world")
  438. (expect 'ido-cr+-update-dynamic-collection
  439. :to-have-been-called))
  440. (it "should do a dynamic update when pressing TAB"
  441. (expect
  442. (with-simulated-input "h TAB -ld RET"
  443. (ido-completing-read+ "Say something: " my-dynamic-collection))
  444. :to-equal
  445. "hello-world")
  446. (expect 'ido-cr+-update-dynamic-collection
  447. :to-have-been-called))
  448. (it "should do a dynamic update when idle"
  449. (expect
  450. (with-simulated-input
  451. '("h"
  452. (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
  453. "-ld RET")
  454. (ido-completing-read+ "Say something: " my-dynamic-collection))
  455. :to-equal
  456. "hello-world")
  457. (expect 'ido-cr+-update-dynamic-collection
  458. :to-have-been-called))
  459. (it "should do a dynamic update when there is only one match remaining"
  460. (expect
  461. (with-simulated-input "hell-ld RET"
  462. (ido-completing-read+ "Say something: " my-dynamic-collection))
  463. :to-equal
  464. "hello-world")
  465. (expect 'ido-cr+-update-dynamic-collection
  466. :to-have-been-called))
  467. (it "should not exit with a unique match if new matches are dynamically added"
  468. (expect
  469. (with-simulated-input '("hell TAB -ld RET")
  470. (ido-completing-read+ "Say something: " my-dynamic-collection))
  471. :to-equal
  472. "hello-world")
  473. (expect 'ido-cr+-update-dynamic-collection
  474. :to-have-been-called))
  475. (it "should exit with a match that is still unique after dynamic updating"
  476. (expect
  477. (with-simulated-input '("helic TAB")
  478. (ido-completing-read+ "Say something: " my-dynamic-collection))
  479. :to-equal
  480. "helicopter")
  481. (expect 'ido-cr+-update-dynamic-collection
  482. :to-have-been-called))
  483. (it "should suppress errors raised by dynamic completion updates"
  484. (let ((collection
  485. (completion-table-dynamic
  486. (lambda (text)
  487. (cond
  488. ((equal text "")
  489. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill"))
  490. (t (error "This collection throws an error on a nonempty prefix"))))))
  491. ;; The test framework uses the debugger to catch error
  492. ;; stack traces, but we want to run this code as if it
  493. ;; was not being debugged.
  494. (debug-on-error nil))
  495. (expect
  496. (with-simulated-input '("hell TAB RET")
  497. (ido-completing-read+ "Say something: " collection))
  498. :to-equal
  499. "hello")))
  500. (it "should respect `ido-restrict-to-matches' when doing dynamic updates"
  501. (assume (version<= "25" emacs-version))
  502. (let ((collection
  503. (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg"
  504. "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg"
  505. "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg"
  506. "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh"
  507. "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh"
  508. "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh"
  509. "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii"
  510. "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii"
  511. "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii")))
  512. ;; Test the internal function
  513. (expect
  514. (ido-cr+-apply-restrictions
  515. collection
  516. (list (cons nil "bbb")
  517. (cons nil "eee")))
  518. :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii"))
  519. ;; First verify it without a dynamic collection
  520. (expect
  521. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  522. (ido-completing-read+
  523. "Pick: " collection nil t nil nil (car collection)))
  524. :to-equal "bbb-eee-ggg")
  525. (expect
  526. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  527. (ido-completing-read+
  528. "Pick: " collection nil t nil nil (car collection)))
  529. :to-equal "bbb-eee-ggg")
  530. ;; Now test the same with a dynamic collection
  531. (expect
  532. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  533. (ido-completing-read+
  534. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  535. :to-equal "bbb-eee-ggg")
  536. (expect
  537. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  538. (ido-completing-read+
  539. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  540. :to-equal "bbb-eee-ggg"))))
  541. (describe "with unusual inputs"
  542. (it "should accept a COLLECTION of symbols"
  543. (expect
  544. (with-simulated-input "g RET"
  545. (ido-completing-read+ "Prompt: " '(blue yellow green)))
  546. :to-equal "green"))
  547. (it "should accept a mix of strings and symbols in COLLECTION"
  548. (expect
  549. (with-simulated-input "g RET"
  550. (ido-completing-read+ "Prompt: " '(blue "yellow" green)))
  551. :to-equal "green"))
  552. (it "should accept symbols in DEF"
  553. (expect
  554. (with-simulated-input "RET"
  555. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '(brown "green")))
  556. :to-equal "brown"))
  557. (it "should accept an alist COLLECTION"
  558. (expect
  559. (with-simulated-input "RET"
  560. (ido-completing-read+
  561. "Prompt: "
  562. '(("blue" . blue-value)
  563. ("yellow" . yellow-value)
  564. (green . green-value))
  565. nil nil nil nil "green"))
  566. :to-equal "green"))
  567. (it "should accept a hash table COLLECTION"
  568. (expect
  569. (with-simulated-input "RET"
  570. (let ((collection (make-hash-table)))
  571. (puthash "blue" 'blue-value collection)
  572. (puthash "yellow" 'yellow-value collection)
  573. (puthash 'green 'green-value collection)
  574. (ido-completing-read+ "Prompt: " collection nil nil nil nil "green")))
  575. :to-equal "green"))
  576. (it "should accept an obarray COLLECTION"
  577. (expect
  578. (with-simulated-input "forward-char RET"
  579. (ido-completing-read+ "Prompt: " obarray #'commandp
  580. t nil nil "backward-char"))
  581. :to-equal "forward-char"))))
  582. (describe "ido-ubiquitous-mode"
  583. ;; Set up a test command that calls `completing-read'
  584. (before-all
  585. (setf (symbol-function 'test-command)
  586. (lambda ()
  587. (interactive)
  588. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  589. ;; Delete the test command
  590. (after-all
  591. (setf (symbol-function 'test-command) nil))
  592. ;; Verify that the mode can be activated
  593. (it "should enable itself properly"
  594. (expect
  595. (progn
  596. (ido-ubiquitous-mode 1)
  597. (with-simulated-input "g RET"
  598. (command-execute 'test-command)))
  599. :to-equal "green"))
  600. (it "should disable itself properly"
  601. (expect
  602. (progn
  603. (ido-ubiquitous-mode 0)
  604. (with-simulated-input "g RET"
  605. (command-execute 'test-command)))
  606. :to-equal "g"))
  607. (describe "with `ido-cr+-disable-list'"
  608. (before-all
  609. (setf (symbol-function 'disabled-command)
  610. (lambda (arg)
  611. (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
  612. arg)
  613. (symbol-function 'disabled-function)
  614. (lambda ()
  615. (completing-read "Prompt: " '("blue" "yellow" "green")))
  616. (symbol-function 'cmd-that-calls-disabled-function)
  617. (lambda ()
  618. (interactive)
  619. (funcall 'disabled-function))
  620. (symbol-function 'disabled-collection)
  621. (collection-as-function '("blue" "yellow" "green"))))
  622. (after-all
  623. (setf (symbol-function 'disabled-command) nil
  624. (symbol-function 'disabled-function) nil
  625. (symbol-function 'cmd-that-calls-disabled-function) nil
  626. (symbol-function 'disabled-collection) nil))
  627. ;; First verify that they work normally before disabling them
  628. (describe "when the specified functions are not disabled"
  629. (it "should not affect a non-disabled command"
  630. (expect
  631. (with-simulated-input "g RET"
  632. (call-interactively 'disabled-command))
  633. :to-equal "green"))
  634. (it "should not affect a non-disabled function"
  635. (expect
  636. (with-simulated-input "g RET"
  637. (call-interactively 'cmd-that-calls-disabled-function))
  638. :to-equal "green"))
  639. (it "should not affect a non-disabled collection"
  640. (expect
  641. (with-simulated-input "g RET"
  642. (ido-completing-read+ "Prompt: " 'disabled-collection))
  643. :to-equal "green")))
  644. (describe "when the specified functions are disabled"
  645. (before-each
  646. (setq ido-cr+-disable-list
  647. (append '(disabled-command
  648. disabled-function
  649. disabled-collection)
  650. ido-cr+-disable-list)))
  651. (it "should prevent ido in a disabled command"
  652. (expect
  653. (with-simulated-input "g RET"
  654. (call-interactively 'disabled-command))
  655. :to-equal "g"))
  656. (it "should prevent ido in a disabled function"
  657. (expect
  658. (with-simulated-input "g RET"
  659. (call-interactively 'cmd-that-calls-disabled-function))
  660. :to-equal "g"))
  661. (it "should prevent ido with a disabled collection"
  662. (expect
  663. (with-simulated-input "g RET"
  664. (ido-completing-read+ "Prompt: " 'disabled-collection))
  665. :to-equal "g")))
  666. (describe "when updating ido-cr+"
  667. (before-each
  668. (spy-on 'ido-cr+-update-disable-list :and-call-through))
  669. (it "should update the disable list when `ido-cr+-auto-update-disable-list' is t"
  670. (assume ido-cr+-disable-list)
  671. (let ((orig-disable-list ido-cr+-disable-list))
  672. (customize-set-variable 'ido-cr+-auto-update-disable-list t)
  673. (customize-set-variable 'ido-cr+-disable-list nil)
  674. (ido-cr+-maybe-update-disable-list)
  675. (expect 'ido-cr+-update-disable-list :to-have-been-called)
  676. (expect ido-cr+-disable-list :to-have-same-items-as orig-disable-list)))
  677. (it "should not update the disable list when `ido-cr+-auto-update-disable-list' is nil"
  678. (assume ido-cr+-disable-list)
  679. (let ((orig-disable-list ido-cr+-disable-list))
  680. (customize-set-variable 'ido-cr+-auto-update-disable-list nil)
  681. (customize-set-variable 'ido-cr+-disable-list nil)
  682. (ido-cr+-maybe-update-disable-list)
  683. (expect 'ido-cr+-update-disable-list :not :to-have-been-called)
  684. (expect ido-cr+-disable-list :to-have-same-items-as nil)))
  685. (it "should notify about disable list updates when `ido-cr+-auto-update-disable-list' is `notify'"
  686. (assume ido-cr+-disable-list)
  687. (spy-on 'display-warning)
  688. (let ((orig-disable-list ido-cr+-disable-list))
  689. (customize-set-variable 'ido-cr+-auto-update-disable-list 'notify)
  690. (customize-set-variable 'ido-cr+-disable-list nil)
  691. (ido-cr+-maybe-update-disable-list)
  692. (expect 'ido-cr+-update-disable-list :not :to-have-been-called)
  693. (expect 'display-warning :to-have-been-called)
  694. (expect ido-cr+-disable-list :to-have-same-items-as nil)))))
  695. (describe "with `ido-cr+-allow-list'"
  696. (before-all
  697. (setf (symbol-function 'allowed-command)
  698. (lambda (arg)
  699. (interactive
  700. (list
  701. (completing-read "Prompt: " '("blue" "yellow" "green"))))
  702. arg)
  703. (symbol-function 'allowed-function)
  704. (lambda ()
  705. (completing-read "Prompt: " '("blue" "yellow" "green")))
  706. (symbol-function 'cmd-that-calls-allowed-function)
  707. (lambda ()
  708. (interactive)
  709. (funcall 'allowed-function))
  710. (symbol-function 'allowed-collection)
  711. (lambda (string pred action)
  712. (complete-with-action action '("blue" "yellow" "green") string pred))))
  713. (after-all
  714. (setf (symbol-function 'allowed-command) nil
  715. (symbol-function 'allowed-function) nil
  716. (symbol-function 'cmd-that-calls-allowed-function) nil
  717. (symbol-function 'allowed-collection) nil))
  718. (describe "when the allow list is inactive (i.e. everything is allowed)"
  719. (before-each
  720. (setq ido-cr+-allow-list nil))
  721. (it "should enable ido in a command"
  722. (expect
  723. (with-simulated-input "g RET"
  724. (call-interactively 'allowed-command))
  725. :to-equal "green"))
  726. (it "should enable ido in a function"
  727. (expect
  728. (with-simulated-input "g RET"
  729. (call-interactively 'cmd-that-calls-allowed-function))
  730. :to-equal "green"))
  731. (it "should enable ido for a collection"
  732. (expect
  733. (with-simulated-input "g RET"
  734. (ido-completing-read+ "Prompt: " 'allowed-collection))
  735. :to-equal "green")))
  736. (describe "when the specified functions are allowed"
  737. (before-each
  738. (setq ido-cr+-allow-list
  739. (append '(allowed-command
  740. allowed-function
  741. allowed-collection)
  742. ido-cr+-allow-list)))
  743. (it "should enable ido in an allowed command"
  744. (expect
  745. (with-simulated-input "g RET"
  746. (call-interactively 'allowed-command))
  747. :to-equal "green"))
  748. (it "should enable ido in an allowed function"
  749. (expect
  750. (with-simulated-input "g RET"
  751. (call-interactively 'cmd-that-calls-allowed-function))
  752. :to-equal "green"))
  753. (it "should enable ido for an allowed collection"
  754. (expect
  755. (with-simulated-input "g RET"
  756. (ido-completing-read+ "Prompt: " 'allowed-collection))
  757. :to-equal "green")))
  758. (describe "when the allow list is active but empty (i.e. nothing allowed)"
  759. (before-each
  760. (setq ido-cr+-allow-list (list nil)))
  761. (it "should prevent ido in a command"
  762. (expect
  763. (with-simulated-input "g RET"
  764. (call-interactively 'allowed-command))
  765. :to-equal "g"))
  766. (it "should prevent ido in a function"
  767. (expect
  768. (with-simulated-input "g RET"
  769. (call-interactively 'cmd-that-calls-allowed-function))
  770. :to-equal "g"))
  771. (it "should prevent ido for a collection"
  772. (expect
  773. (with-simulated-input "g RET"
  774. (ido-completing-read+ "Prompt: " 'allowed-collection))
  775. :to-equal "g"))))
  776. (describe "with `ido-cr+-nil-def-alternate-behavior-list'"
  777. (before-all
  778. (setf (symbol-function 'def-nil-command)
  779. (lambda (arg)
  780. (interactive
  781. (list
  782. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))
  783. arg)
  784. (symbol-function 'def-nil-function)
  785. (lambda ()
  786. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))
  787. (symbol-function 'cmd-that-calls-def-nil-function)
  788. (lambda ()
  789. (interactive)
  790. (funcall 'def-nil-function))
  791. (symbol-function 'def-nil-collection)
  792. (lambda (string pred action)
  793. (complete-with-action action '("blue" "yellow" "green") string pred))))
  794. (after-all
  795. (setf (symbol-function 'def-nil-command) nil
  796. (symbol-function 'def-nil-function) nil
  797. (symbol-function 'cmd-that-calls-def-nil-function) nil
  798. (symbol-function 'def-nil-collection) nil))
  799. (describe "when the specified functions are not in the list"
  800. (before-each
  801. (setq ido-cr+-nil-def-alternate-behavior-list nil))
  802. (it "should use empty string default in a command"
  803. (expect
  804. (with-simulated-input "RET"
  805. (call-interactively 'def-nil-command))
  806. :to-equal ""))
  807. (it "should use empty string default in a function"
  808. (expect
  809. (with-simulated-input "RET"
  810. (call-interactively 'cmd-that-calls-def-nil-function))
  811. :to-equal ""))
  812. (it "should use empty string default for a collection"
  813. (expect
  814. (with-simulated-input "RET"
  815. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  816. :to-equal "")))
  817. (describe "when the specified functions are in the list"
  818. (before-each
  819. (setq ido-cr+-nil-def-alternate-behavior-list
  820. (append '(def-nil-command
  821. def-nil-function
  822. def-nil-collection)
  823. ido-cr+-nil-def-alternate-behavior-list)))
  824. (it "should not use empty string default in a command"
  825. (expect
  826. (with-simulated-input "RET"
  827. (call-interactively 'def-nil-command))
  828. :to-equal "blue"))
  829. (it "should not use empty string default in a function"
  830. (expect
  831. (with-simulated-input "RET"
  832. (call-interactively 'cmd-that-calls-def-nil-function))
  833. :to-equal "blue"))
  834. (it "should not use empty string default for a collection"
  835. (expect
  836. (with-simulated-input "RET"
  837. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  838. :to-equal "blue"))))
  839. ;; Test is currently disabled pending additional information
  840. (xit "should not hang or error when deleting characters in `org-refile' (issue #152)"
  841. (expect
  842. (progn
  843. (ido-ubiquitous-mode 1)
  844. (save-excursion
  845. (with-temp-buffer
  846. (org-mode)
  847. (insert (s-trim "
  848. * Heading 1
  849. ** Subheading 1.1
  850. ** Subheading 1.2
  851. ** Subheading 1.3
  852. * Heading 2
  853. * Heading 3
  854. "))
  855. (goto-char (point-max))
  856. ;; TODO Figure out what else needs to be set up to call
  857. ;; `org-refile'
  858. (with-simulated-input
  859. "Heading DEL DEL DEL DEL DEL RET"
  860. (command-execute 'org-refile)))))
  861. :not :to-throw)))
  862. (describe "regressions should not occur for"
  863. ;; Disabled because I think the nix CI emacs has no info pages, so
  864. ;; the completion for `Info-menu' has nothing to do. However, this
  865. ;; should be thoroughly fixed by now.
  866. (xit "issue #151: should not hang or error when cycling matches in `Info-menu'"
  867. (expect
  868. (progn
  869. (ido-ubiquitous-mode 1)
  870. (with-temp-info-buffer
  871. (with-simulated-input
  872. '((ido-next-match)
  873. (wsi-simulate-idle-time 5)
  874. (ido-next-match)
  875. (wsi-simulate-idle-time 5)
  876. (ido-next-match)
  877. (wsi-simulate-idle-time 5)
  878. (ido-next-match)
  879. (wsi-simulate-idle-time 5)
  880. "RET")
  881. (command-execute 'Info-menu))))
  882. :not :to-throw))
  883. (it "issue #153: should preserve the selected item when doing a deferred dynamic update"
  884. (expect
  885. (with-simulated-input
  886. '("Emacs"
  887. (ido-next-match)
  888. (wsi-simulate-idle-time 5)
  889. "RET")
  890. (ido-completing-read+
  891. "Choose: "
  892. (collection-as-function '("Emacs" "Emacs A" "Emacs B" "Emacs C"))))
  893. :to-equal "Emacs A"))))
  894. ;;; test-ido-completing-read+.el ends here