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