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

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