(defvar electric-layout-rules nil
"List of rules saying where to automatically insert newlines.
-Each rule has the form (CHAR . WHERE) where CHAR is the char that
-was just inserted and WHERE specifies where to insert newlines
-and can be:
+Each rule has the form (MATCHER . WHERE) where MATCHER examines
+the state of the buffer after a certain character was inserted
+and WHERE specifies where to insert newlines.
+
+MATCHER can be a character CHAR or a boolean function of no
+arguments. The rule matches if the character just inserted was
+CHAR or if the function return non-nil.
+
+WHERE and can be:
* one of the symbols `before', `after', `around' and `after-stay';
* a function of no arguments that returns one of the previous
values.
-Each symbol specifies where in relation to CHAR the newline
-character(s) should be inserted. `after-stay' means insert a
-newline after CHAR but stay in the same place.
+Each symbol specifies where, in relation to the position POS of
+the character inserted, the newline character(s) should be
+inserted. `after-stay' means insert a newline after POS but stay
+in the same place.
If multiple rules match, only first one is executed.")
;; for edebug's sake, a separate function
(defun electric-layout-post-self-insert-function-1 ()
- (let (pos
- (rule (cdr (assq last-command-event electric-layout-rules))))
+ (let* (pos
+ probe
+ (rules electric-layout-rules)
+ (rule
+ (catch 'done
+ (while (setq probe (pop rules))
+ (cond ((and (consp probe)
+ (or (eq (car probe) last-command-event)
+ (and (functionp (car probe))
+ (funcall (car probe)))))
+ (throw 'done (cdr probe)))
+ ((functionp probe)
+ (throw 'done (funcall probe))))))))
(when (and rule
(setq pos (electric--after-char-pos))
;; Not in a string or comment.
;;; tests for `electric-layout-mode'
(ert-deftest electric-layout-int-main-kernel-style ()
- (save-electric-modes
- (ert-with-test-buffer ()
- (c-mode)
- (electric-layout-local-mode 1)
- (electric-pair-local-mode 1)
- (electric-indent-local-mode 1)
- (setq-local electric-layout-rules
- '((?\{ . (after-stay after))))
- (insert "int main () ")
- (let ((last-command-event ?\{))
- (call-interactively (key-binding `[,last-command-event])))
- (should (equal (buffer-string) "int main () {\n \n}")))))
+ (ert-with-test-buffer ()
+ (c-mode)
+ (electric-layout-local-mode 1)
+ (electric-pair-local-mode 1)
+ (electric-indent-local-mode 1)
+ (setq-local electric-layout-rules
+ '((?\{ . (after-stay after))))
+ (insert "int main () ")
+ (let ((last-command-event ?\{))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main () {\n \n}"))))
(ert-deftest electric-layout-int-main-allman-style ()
- (save-electric-modes
- (ert-with-test-buffer ()
- (c-mode)
- (electric-layout-local-mode 1)
- (electric-pair-local-mode 1)
- (electric-indent-local-mode 1)
- (setq-local electric-layout-rules
- '((?\{ . (before after-stay after))))
- (insert "int main () ")
- (let ((last-command-event ?\{))
- (call-interactively (key-binding `[,last-command-event])))
- (should (equal (buffer-string) "int main ()\n{\n \n}")))))
+ (ert-with-test-buffer ()
+ (c-mode)
+ (electric-layout-local-mode 1)
+ (electric-pair-local-mode 1)
+ (electric-indent-local-mode 1)
+ (setq-local electric-layout-rules
+ '((?\{ . (before after-stay after))))
+ (insert "int main () ")
+ (let ((last-command-event ?\{))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main ()\n{\n \n}"))))
(define-derived-mode plainer-c-mode c-mode "pC"
- "A plainer C-mode")
+ "A plainer/saner C-mode with no internal electric machinery."
+ (c-toggle-electric-state -1)
+ (setq-local electric-indent-local-mode-hook nil)
+ (setq-local electric-indent-mode-hook nil)
+ (electric-indent-local-mode 1)
+ (dolist (key '(?\" ?\' ?\{ ?\} ?\( ?\) ?\[ ?\]))
+ (local-set-key (vector key) 'self-insert-command)))
(ert-deftest electric-modes-in-c-mode-with-self-insert-command ()
- (save-electric-modes
- (ert-with-test-buffer ()
- (plainer-c-mode)
- (electric-layout-local-mode 1)
- (electric-pair-local-mode 1)
- (electric-indent-local-mode 1)
- (dolist (key '(?\" ?\' ?\{ ?\} ?\( ?\) ?\[ ?\]))
- (local-set-key (vector key) 'self-insert-command))
- (setq-local electric-layout-rules
- '((?\{ . (before after-stay after))))
- (insert "int main () ")
- (let ((last-command-event ?\{))
- (call-interactively (key-binding `[,last-command-event])))
- (should (equal (buffer-string) "int main ()\n{\n \n}")))))
+ (ert-with-test-buffer ()
+ (plainer-c-mode)
+ (electric-layout-local-mode 1)
+ (electric-pair-local-mode 1)
+ (electric-indent-local-mode 1)
+ (setq-local electric-layout-rules
+ '((?\{ . (before after-stay after))))
+ (insert "int main () ")
+ (let ((last-command-event ?\{))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main ()\n{\n \n}"))))
+
+;; FIXME: The two following tests fail, because the newline simulation
+;; fails to indent the new line. Interactively, they work fine.
+;; Don't know why...
+(ert-deftest electric-pair-mode-newline-between-parens ()
+ :expected-result :failed
+ (ert-with-test-buffer (:name "electric-pair-mode-newline-between-parens")
+ (plainer-c-mode)
+ (electric-layout-local-mode -1) ;; ensure e-l-m mode is off
+ (electric-pair-local-mode 1)
+ (insert-before-markers "int main () {}")
+ (backward-char 1)
+ (let ((last-command-event ?\n))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main ()\n{\n \n}"))))
+
+(ert-deftest electric-layout-mode-newline-between-parens-without-e-p-m ()
+ :expected-result :failed
+ (ert-with-test-buffer (:name "electric-pair-mode-newline-between-parens")
+ (plainer-c-mode)
+ (electric-layout-local-mode 1)
+ (electric-pair-local-mode -1) ;; ensure e-p-m mode is off
+ (electric-indent-local-mode 1)
+ (setq-local electric-layout-rules
+ '((?\n
+ .
+ (lambda ()
+ (when (eq (save-excursion
+ (skip-chars-backward "\t\s")
+ (char-before (1- (point))))
+ (matching-paren (char-after)))
+ '(after-stay))))))
+ (insert "int main () {}")
+ (backward-char 1)
+ (let ((last-command-event ?\n))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main ()\n{\n \n}"))))
(provide 'electric-tests)
;;; electric-tests.el ends here