electric-tests.el 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. ;;; electric-tests.el --- tests for electric.el
  2. ;; Copyright (C) 2013-2017 Free Software Foundation, Inc.
  3. ;; Author: João Távora <joaotavora@gmail.com>
  4. ;; Keywords:
  5. ;; This program is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; Tests for Electric Pair mode.
  17. ;; TODO: Add tests for other Electric-* functionality
  18. ;;; Code:
  19. (require 'ert)
  20. (require 'ert-x)
  21. (require 'electric)
  22. (require 'elec-pair)
  23. (require 'cl-lib)
  24. (defun call-with-saved-electric-modes (fn)
  25. (let ((saved-electric (if electric-pair-mode 1 -1))
  26. (saved-layout (if electric-layout-mode 1 -1))
  27. (saved-indent (if electric-indent-mode 1 -1)))
  28. (electric-pair-mode -1)
  29. (electric-layout-mode -1)
  30. (electric-indent-mode -1)
  31. (unwind-protect
  32. (funcall fn)
  33. (electric-pair-mode saved-electric)
  34. (electric-indent-mode saved-indent)
  35. (electric-layout-mode saved-layout))))
  36. (defmacro save-electric-modes (&rest body)
  37. (declare (indent defun) (debug t))
  38. `(call-with-saved-electric-modes #'(lambda () ,@body)))
  39. (defun electric-pair-test-for (fixture where char expected-string
  40. expected-point mode bindings fixture-fn)
  41. (with-temp-buffer
  42. (funcall mode)
  43. (insert fixture)
  44. (save-electric-modes
  45. (let ((last-command-event char)
  46. (transient-mark-mode 'lambda))
  47. (goto-char where)
  48. (funcall fixture-fn)
  49. (cl-progv
  50. (mapcar #'car bindings)
  51. (mapcar #'cdr bindings)
  52. (call-interactively (key-binding `[,last-command-event])))))
  53. (should (equal (buffer-substring-no-properties (point-min) (point-max))
  54. expected-string))
  55. (should (equal (point)
  56. expected-point))))
  57. (eval-when-compile
  58. (defun electric-pair-define-test-form (name fixture
  59. char
  60. pos
  61. expected-string
  62. expected-point
  63. skip-pair-string
  64. prefix
  65. suffix
  66. extra-desc
  67. mode
  68. bindings
  69. fixture-fn)
  70. (let* ((expected-string-and-point
  71. (if skip-pair-string
  72. (with-temp-buffer
  73. (cl-progv
  74. ;; FIXME: avoid `eval'
  75. (mapcar #'car (eval bindings))
  76. (mapcar #'cdr (eval bindings))
  77. (funcall mode)
  78. (insert fixture)
  79. (goto-char (1+ pos))
  80. (insert char)
  81. (cond ((eq (aref skip-pair-string pos)
  82. ?p)
  83. (insert (cadr (electric-pair-syntax-info char)))
  84. (backward-char 1))
  85. ((eq (aref skip-pair-string pos)
  86. ?s)
  87. (delete-char -1)
  88. (forward-char 1)))
  89. (list
  90. (buffer-substring-no-properties (point-min) (point-max))
  91. (point))))
  92. (list expected-string expected-point)))
  93. (expected-string (car expected-string-and-point))
  94. (expected-point (cadr expected-string-and-point))
  95. (fixture (format "%s%s%s" prefix fixture suffix))
  96. (expected-string (format "%s%s%s" prefix expected-string suffix))
  97. (expected-point (+ (length prefix) expected-point))
  98. (pos (+ (length prefix) pos)))
  99. `(ert-deftest ,(intern (format "electric-pair-%s-at-point-%s-in-%s%s"
  100. name
  101. (1+ pos)
  102. mode
  103. extra-desc))
  104. ()
  105. ,(format "With |%s|, try input %c at point %d. \
  106. Should %s |%s| and point at %d"
  107. fixture
  108. char
  109. (1+ pos)
  110. (if (string= fixture expected-string)
  111. "stay"
  112. "become")
  113. (replace-regexp-in-string "\n" "\\\\n" expected-string)
  114. expected-point)
  115. (electric-pair-test-for ,fixture
  116. ,(1+ pos)
  117. ,char
  118. ,expected-string
  119. ,expected-point
  120. ',mode
  121. ,bindings
  122. ,fixture-fn)))))
  123. (cl-defmacro define-electric-pair-test
  124. (name fixture
  125. input
  126. &key
  127. skip-pair-string
  128. expected-string
  129. expected-point
  130. bindings
  131. (modes '(quote (ruby-mode c++-mode)))
  132. (test-in-comments t)
  133. (test-in-strings t)
  134. (test-in-code t)
  135. (fixture-fn #'(lambda ()
  136. (electric-pair-mode 1))))
  137. `(progn
  138. ,@(cl-loop
  139. for mode in (eval modes) ;FIXME: avoid `eval'
  140. append
  141. (cl-loop
  142. for (prefix suffix extra-desc) in
  143. (append (if test-in-comments
  144. `((,(with-temp-buffer
  145. (funcall mode)
  146. (insert "z")
  147. (comment-region (point-min) (point-max))
  148. (buffer-substring-no-properties (point-min)
  149. (1- (point-max))))
  150. ""
  151. "-in-comments")))
  152. (if test-in-strings
  153. `(("\"" "\"" "-in-strings")))
  154. (if test-in-code
  155. `(("" "" ""))))
  156. append
  157. (cl-loop
  158. for char across input
  159. for pos from 0
  160. unless (eq char ?-)
  161. collect (electric-pair-define-test-form
  162. name
  163. fixture
  164. (aref input pos)
  165. pos
  166. expected-string
  167. expected-point
  168. skip-pair-string
  169. prefix
  170. suffix
  171. extra-desc
  172. mode
  173. bindings
  174. fixture-fn))))))
  175. ;;; Basic pairs and skips
  176. ;;;
  177. (define-electric-pair-test balanced-situation
  178. " (()) " "(((((((" :skip-pair-string "ppppppp"
  179. :modes '(ruby-mode))
  180. (define-electric-pair-test too-many-openings
  181. " ((()) " "(((((((" :skip-pair-string "ppppppp")
  182. (define-electric-pair-test too-many-closings
  183. " (())) " "(((((((" :skip-pair-string "------p")
  184. (define-electric-pair-test too-many-closings-2
  185. "() ) " "---(---" :skip-pair-string "-------")
  186. (define-electric-pair-test too-many-closings-3
  187. ")() " "(------" :skip-pair-string "-------")
  188. (define-electric-pair-test balanced-autoskipping
  189. " (()) " "---))--" :skip-pair-string "---ss--")
  190. (define-electric-pair-test too-many-openings-autoskipping
  191. " ((()) " "----))-" :skip-pair-string "-------")
  192. (define-electric-pair-test too-many-closings-autoskipping
  193. " (())) " "---)))-" :skip-pair-string "---sss-")
  194. ;;; Mixed parens
  195. ;;;
  196. (define-electric-pair-test mixed-paren-1
  197. " ()] " "-(-(---" :skip-pair-string "-p-p---")
  198. (define-electric-pair-test mixed-paren-2
  199. " [() " "-(-()--" :skip-pair-string "-p-ps--")
  200. (define-electric-pair-test mixed-paren-3
  201. " (]) " "-(-()--" :skip-pair-string "---ps--")
  202. (define-electric-pair-test mixed-paren-4
  203. " ()] " "---)]--" :skip-pair-string "---ss--")
  204. (define-electric-pair-test mixed-paren-5
  205. " [() " "----(--" :skip-pair-string "----p--")
  206. (define-electric-pair-test find-matching-different-paren-type
  207. " ()] " "-[-----" :skip-pair-string "-------")
  208. (define-electric-pair-test find-matching-different-paren-type-inside-list
  209. "( ()]) " "-[-----" :skip-pair-string "-------")
  210. (define-electric-pair-test ignore-different-nonmatching-paren-type
  211. "( ()]) " "-(-----" :skip-pair-string "-p-----")
  212. (define-electric-pair-test autopair-keep-least-amount-of-mixed-unbalance
  213. "( ()] " "-(-----" :skip-pair-string "-p-----")
  214. (define-electric-pair-test dont-autopair-to-resolve-mixed-unbalance
  215. "( ()] " "-[-----" :skip-pair-string "-------")
  216. (define-electric-pair-test autopair-so-as-not-to-worsen-unbalance-situation
  217. "( (]) " "-[-----" :skip-pair-string "-p-----")
  218. (define-electric-pair-test skip-over-partially-balanced
  219. " [([]) " "-----)---" :skip-pair-string "-----s---")
  220. (define-electric-pair-test only-skip-over-at-least-partially-balanced-stuff
  221. " [([()) " "-----))--" :skip-pair-string "-----s---")
  222. ;;; Quotes
  223. ;;;
  224. (define-electric-pair-test pair-some-quotes-skip-others
  225. " \"\" " "-\"\"-----" :skip-pair-string "-ps------"
  226. :test-in-strings nil
  227. :bindings `((electric-pair-text-syntax-table
  228. . ,prog-mode-syntax-table)))
  229. (define-electric-pair-test skip-single-quotes-in-ruby-mode
  230. " '' " "--'-" :skip-pair-string "--s-"
  231. :modes '(ruby-mode)
  232. :test-in-comments nil
  233. :test-in-strings nil
  234. :bindings `((electric-pair-text-syntax-table
  235. . ,prog-mode-syntax-table)))
  236. (define-electric-pair-test leave-unbalanced-quotes-alone
  237. " \"' " "-\"'-" :skip-pair-string "----"
  238. :modes '(ruby-mode)
  239. :test-in-strings nil
  240. :bindings `((electric-pair-text-syntax-table
  241. . ,prog-mode-syntax-table)))
  242. (define-electric-pair-test leave-unbalanced-quotes-alone-2
  243. " \"\\\"' " "-\"--'-" :skip-pair-string "------"
  244. :modes '(ruby-mode)
  245. :test-in-strings nil
  246. :bindings `((electric-pair-text-syntax-table
  247. . ,prog-mode-syntax-table)))
  248. (define-electric-pair-test leave-unbalanced-quotes-alone-3
  249. " foo\\''" "'------" :skip-pair-string "-------"
  250. :modes '(ruby-mode)
  251. :test-in-strings nil
  252. :bindings `((electric-pair-text-syntax-table
  253. . ,prog-mode-syntax-table)))
  254. (define-electric-pair-test inhibit-if-strings-mismatched
  255. "\"foo\"\"bar" "\""
  256. :expected-string "\"\"foo\"\"bar"
  257. :expected-point 2
  258. :test-in-strings nil
  259. :bindings `((electric-pair-text-syntax-table
  260. . ,prog-mode-syntax-table)))
  261. (define-electric-pair-test inhibit-in-mismatched-string-inside-ruby-comments
  262. "foo\"\"
  263. #
  264. # \"bar\"
  265. # \" \"
  266. # \"
  267. #
  268. baz\"\""
  269. "\""
  270. :modes '(ruby-mode)
  271. :test-in-strings nil
  272. :test-in-comments nil
  273. :expected-point 19
  274. :expected-string
  275. "foo\"\"
  276. #
  277. # \"bar\"\"
  278. # \" \"
  279. # \"
  280. #
  281. baz\"\""
  282. :fixture-fn #'(lambda () (goto-char (point-min)) (search-forward "bar")))
  283. (define-electric-pair-test inhibit-in-mismatched-string-inside-c-comments
  284. "foo\"\"/*
  285. \"bar\"
  286. \" \"
  287. \"
  288. */baz\"\""
  289. "\""
  290. :modes '(c-mode)
  291. :test-in-strings nil
  292. :test-in-comments nil
  293. :expected-point 18
  294. :expected-string
  295. "foo\"\"/*
  296. \"bar\"\"
  297. \" \"
  298. \"
  299. */baz\"\""
  300. :fixture-fn #'(lambda () (goto-char (point-min)) (search-forward "bar")))
  301. ;;; More quotes, but now don't bind `electric-pair-text-syntax-table'
  302. ;;; to `prog-mode-syntax-table'. Use the defaults for
  303. ;;; `electric-pair-pairs' and `electric-pair-text-pairs'.
  304. ;;;
  305. (define-electric-pair-test pairing-skipping-quotes-in-code
  306. " \"\" " "-\"\"-----" :skip-pair-string "-ps------"
  307. :test-in-strings nil
  308. :test-in-comments nil)
  309. (define-electric-pair-test skipping-quotes-in-comments
  310. " \"\" " "--\"-----" :skip-pair-string "--s------"
  311. :test-in-strings nil)
  312. ;;; Skipping over whitespace
  313. ;;;
  314. (define-electric-pair-test whitespace-jumping
  315. " ( ) " "--))))---" :expected-string " ( ) " :expected-point 8
  316. :bindings '((electric-pair-skip-whitespace . t)))
  317. (define-electric-pair-test whitespace-chomping
  318. " ( ) " "--)------" :expected-string " () " :expected-point 4
  319. :bindings '((electric-pair-skip-whitespace . chomp)))
  320. (define-electric-pair-test whitespace-chomping-2
  321. " ( \n\t\t\n ) " "--)------" :expected-string " () " :expected-point 4
  322. :bindings '((electric-pair-skip-whitespace . chomp))
  323. :test-in-comments nil)
  324. (define-electric-pair-test whitespace-chomping-dont-cross-comments
  325. " ( \n\t\t\n ) " "--)------" :expected-string " () \n\t\t\n ) "
  326. :expected-point 4
  327. :bindings '((electric-pair-skip-whitespace . chomp))
  328. :test-in-strings nil
  329. :test-in-code nil
  330. :test-in-comments t)
  331. (define-electric-pair-test whitespace-skipping-for-quotes-not-outside
  332. " \" \"" "\"-----" :expected-string "\"\" \" \""
  333. :expected-point 2
  334. :bindings '((electric-pair-skip-whitespace . chomp))
  335. :test-in-strings nil
  336. :test-in-code t
  337. :test-in-comments nil)
  338. (define-electric-pair-test whitespace-skipping-for-quotes-only-inside
  339. " \" \"" "---\"--" :expected-string " \"\""
  340. :expected-point 5
  341. :bindings '((electric-pair-skip-whitespace . chomp))
  342. :test-in-strings nil
  343. :test-in-code t
  344. :test-in-comments nil)
  345. (define-electric-pair-test whitespace-skipping-quotes-not-without-proper-syntax
  346. " \" \"" "---\"--" :expected-string " \"\"\" \""
  347. :expected-point 5
  348. :modes '(text-mode)
  349. :bindings '((electric-pair-skip-whitespace . chomp))
  350. :test-in-strings nil
  351. :test-in-code t
  352. :test-in-comments nil)
  353. ;;; Pairing arbitrary characters
  354. ;;;
  355. (define-electric-pair-test angle-brackets-everywhere
  356. "<>" "<>" :skip-pair-string "ps"
  357. :bindings '((electric-pair-pairs . ((?\< . ?\>)))))
  358. (define-electric-pair-test angle-brackets-everywhere-2
  359. "(<>" "-<>" :skip-pair-string "-ps"
  360. :bindings '((electric-pair-pairs . ((?\< . ?\>)))))
  361. (defvar electric-pair-test-angle-brackets-table
  362. (let ((table (make-syntax-table prog-mode-syntax-table)))
  363. (modify-syntax-entry ?\< "(>" table)
  364. (modify-syntax-entry ?\> ")<`" table)
  365. table))
  366. (define-electric-pair-test angle-brackets-pair
  367. "<>" "<" :expected-string "<><>" :expected-point 2
  368. :test-in-code nil
  369. :bindings `((electric-pair-text-syntax-table
  370. . ,electric-pair-test-angle-brackets-table)))
  371. (define-electric-pair-test angle-brackets-skip
  372. "<>" "->" :expected-string "<>" :expected-point 3
  373. :test-in-code nil
  374. :bindings `((electric-pair-text-syntax-table
  375. . ,electric-pair-test-angle-brackets-table)))
  376. (define-electric-pair-test pair-backtick-and-quote-in-comments
  377. ";; " "---`" :expected-string ";; `'" :expected-point 5
  378. :test-in-comments nil
  379. :test-in-strings nil
  380. :modes '(emacs-lisp-mode)
  381. :bindings '((electric-pair-text-pairs . ((?\` . ?\')))))
  382. (define-electric-pair-test skip-backtick-and-quote-in-comments
  383. ";; `foo'" "-------'" :expected-string ";; `foo'" :expected-point 9
  384. :test-in-comments nil
  385. :test-in-strings nil
  386. :modes '(emacs-lisp-mode)
  387. :bindings '((electric-pair-text-pairs . ((?\` . ?\')))))
  388. (define-electric-pair-test pair-backtick-and-quote-in-strings
  389. "\"\"" "-`" :expected-string "\"`'\"" :expected-point 3
  390. :test-in-comments nil
  391. :test-in-strings nil
  392. :modes '(emacs-lisp-mode)
  393. :bindings '((electric-pair-text-pairs . ((?\` . ?\')))))
  394. (define-electric-pair-test skip-backtick-and-quote-in-strings
  395. "\"`'\"" "--'" :expected-string "\"`'\"" :expected-point 4
  396. :test-in-comments nil
  397. :test-in-strings nil
  398. :modes '(emacs-lisp-mode)
  399. :bindings '((electric-pair-text-pairs . ((?\` . ?\')))))
  400. (define-electric-pair-test skip-backtick-and-quote-in-strings-2
  401. " \"`'\"" "----'" :expected-string " \"`'\"" :expected-point 6
  402. :test-in-comments nil
  403. :test-in-strings nil
  404. :modes '(emacs-lisp-mode)
  405. :bindings '((electric-pair-text-pairs . ((?\` . ?\')))))
  406. ;;; `js-mode' has `electric-layout-rules' for '{ and '}
  407. ;;;
  408. (define-electric-pair-test js-mode-braces
  409. "" "{" :expected-string "{}" :expected-point 2
  410. :modes '(js-mode)
  411. :fixture-fn #'(lambda ()
  412. (electric-pair-mode 1)))
  413. (define-electric-pair-test js-mode-braces-with-layout
  414. "" "{" :expected-string "{\n\n}" :expected-point 3
  415. :modes '(js-mode)
  416. :test-in-comments nil
  417. :test-in-strings nil
  418. :fixture-fn #'(lambda ()
  419. (electric-layout-mode 1)
  420. (electric-pair-mode 1)))
  421. (define-electric-pair-test js-mode-braces-with-layout-and-indent
  422. "" "{" :expected-string "{\n \n}" :expected-point 7
  423. :modes '(js-mode)
  424. :test-in-comments nil
  425. :test-in-strings nil
  426. :fixture-fn #'(lambda ()
  427. (electric-pair-mode 1)
  428. (electric-indent-mode 1)
  429. (electric-layout-mode 1)))
  430. ;;; Backspacing
  431. ;;; TODO: better tests
  432. ;;;
  433. (ert-deftest electric-pair-backspace-1 ()
  434. (save-electric-modes
  435. (with-temp-buffer
  436. (insert "()")
  437. (goto-char 2)
  438. (electric-pair-delete-pair 1)
  439. (should (equal "" (buffer-string))))))
  440. ;;; Electric newlines between pairs
  441. ;;; TODO: better tests
  442. (ert-deftest electric-pair-open-extra-newline ()
  443. (save-electric-modes
  444. (with-temp-buffer
  445. (c-mode)
  446. (electric-pair-mode 1)
  447. (electric-indent-mode 1)
  448. (insert "int main {}")
  449. (backward-char 1)
  450. (let ((c-basic-offset 4))
  451. (newline 1 t)
  452. (should (equal "int main {\n \n}"
  453. (buffer-string)))
  454. (should (equal (point) (- (point-max) 2)))))))
  455. ;;; Autowrapping
  456. ;;;
  457. (define-electric-pair-test autowrapping-1
  458. "foo" "(" :expected-string "(foo)" :expected-point 2
  459. :fixture-fn #'(lambda ()
  460. (electric-pair-mode 1)
  461. (mark-sexp 1)))
  462. (define-electric-pair-test autowrapping-2
  463. "foo" ")" :expected-string "(foo)" :expected-point 6
  464. :fixture-fn #'(lambda ()
  465. (electric-pair-mode 1)
  466. (mark-sexp 1)))
  467. (define-electric-pair-test autowrapping-3
  468. "foo" ")" :expected-string "(foo)" :expected-point 6
  469. :fixture-fn #'(lambda ()
  470. (electric-pair-mode 1)
  471. (goto-char (point-max))
  472. (skip-chars-backward "\"")
  473. (mark-sexp -1)))
  474. (define-electric-pair-test autowrapping-4
  475. "foo" "(" :expected-string "(foo)" :expected-point 2
  476. :fixture-fn #'(lambda ()
  477. (electric-pair-mode 1)
  478. (goto-char (point-max))
  479. (skip-chars-backward "\"")
  480. (mark-sexp -1)))
  481. (define-electric-pair-test autowrapping-5
  482. "foo" "\"" :expected-string "\"foo\"" :expected-point 2
  483. :fixture-fn #'(lambda ()
  484. (electric-pair-mode 1)
  485. (mark-sexp 1)))
  486. (define-electric-pair-test autowrapping-6
  487. "foo" "\"" :expected-string "\"foo\"" :expected-point 6
  488. :fixture-fn #'(lambda ()
  489. (electric-pair-mode 1)
  490. (goto-char (point-max))
  491. (skip-chars-backward "\"")
  492. (mark-sexp -1)))
  493. (define-electric-pair-test autowrapping-7
  494. "foo" "\"" :expected-string "``foo''" :expected-point 8
  495. :modes '(tex-mode)
  496. :test-in-comments nil
  497. :fixture-fn #'(lambda ()
  498. (electric-pair-mode 1)
  499. (goto-char (point-max))
  500. (skip-chars-backward "\"")
  501. (mark-sexp -1)))
  502. ;;; Electric quotes
  503. (define-electric-pair-test electric-quote-string
  504. "" "'" :expected-string "'" :expected-point 2
  505. :fixture-fn #'electric-quote-local-mode
  506. :bindings '((electric-quote-string . t))
  507. :test-in-comments nil :test-in-strings nil)
  508. (provide 'electric-tests)
  509. ;;; electric-tests.el ends here