[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] master 949295a: Extend electric-layout-mode to handle more
From: |
João Távora |
Subject: |
[Emacs-diffs] master 949295a: Extend electric-layout-mode to handle more complex layouts (bug#33794) |
Date: |
Wed, 2 Jan 2019 07:57:13 -0500 (EST) |
branch: master
commit 949295ae1a8a79a181b2bf614b9c69849f2fd667
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>
Extend electric-layout-mode to handle more complex layouts (bug#33794)
Entries in electric-layout-rules can specify multiple
newline-related actions which are executed in order of appearance.
Also, have it play nice with electric-pair-mode when inserting a
newlines, particularly with electric-pair-open-newline-between-pairs.
Entries in electric-layout-rules can also be functions. Among other
things, the logic behind electric-pair-open-newline-between-pairs
could now be moved to electric-layout-mode, but this commit doesn't do
that yet.
This change was motivated by bug#33794 and is an alternative solution
to the problem reported in that bug.
* lisp/electric.el (electric-layout-rules): Adjust docstring.
(electric-layout-post-self-insert-function): Call
electric-layout-post-self-insert-function-1.
(electric-layout-post-self-insert-function-1): Rename from
electric-layout-post-self-insert-function. Redesign.
(electric-layout-local-mode): New minor mode.
* test/lisp/electric-tests.el (electric-layout-int-main-kernel-style)
(electric-layout-int-main-allman-style)
(electric-modes-in-c-mode-with-self-insert-command)
(electric-pair-mode-newline-between-parens)
(electric-layout-mode-newline-between-parens-without-e-p-m)
(electric-layout-mode-newline-between-parens-without-e-p-m-2): New
tests.
(plainer-c-mode): New helper.
---
lisp/electric.el | 125 ++++++++++++++++++++++++++++++++------------
test/lisp/electric-tests.el | 104 ++++++++++++++++++++++++++++++++++++
2 files changed, 196 insertions(+), 33 deletions(-)
diff --git a/lisp/electric.el b/lisp/electric.el
index 36841bc..e7ebdf5 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -363,45 +363,91 @@ use `electric-indent-local-mode'."
(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: nil, `before', `after', `around', `after-stay', or a
-function of no arguments that returns one of those symbols.
+Each rule has the form (CHAR . WHERE), the rule matching if the
+character just inserted was CHAR. WHERE specifies where to
+insert newlines, and can be:
-The symbols specify 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.")
+* one of the symbols `before', `after', `around', `after-stay',
+ or nil.
+
+* a list of the preceding symbols, processed in order of
+ appearance to insert multiple newlines;
+
+* a function of no arguments that returns one of the previous
+ values.
+
+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.
+
+Instead of the (CHAR . WHERE) form, a rule can also be just a
+function of a single argument, the character just inserted. It
+should return a value compatible with WHERE if the rule matches,
+or nil if it doesn't match.
+
+If multiple rules match, only first one is executed.")
(defun electric-layout-post-self-insert-function ()
- (let* ((rule (cdr (assq last-command-event electric-layout-rules)))
- pos)
+ (when electric-layout-mode
+ (electric-layout-post-self-insert-function-1)))
+
+;; for edebug's sake, a separate function
+(defun electric-layout-post-self-insert-function-1 ()
+ (let* (pos
+ probe
+ (rules electric-layout-rules)
+ (rule
+ (catch 'done
+ (while (setq probe (pop rules))
+ (cond ((and (consp probe)
+ (eq (car probe) last-command-event))
+ (throw 'done (cdr probe)))
+ ((functionp probe)
+ (let ((res
+ (save-excursion
+ (goto-char
+ (or pos (setq pos (electric--after-char-pos))))
+ (funcall probe last-command-event))))
+ (when res (throw 'done res)))))))))
(when (and rule
- (setq pos (electric--after-char-pos))
+ (or pos (setq pos (electric--after-char-pos)))
;; Not in a string or comment.
(not (nth 8 (save-excursion (syntax-ppss pos)))))
- (let ((end (point-marker))
- (sym (if (functionp rule) (funcall rule) rule)))
- (set-marker-insertion-type end (not (eq sym 'after-stay)))
- (goto-char pos)
- (pcase sym
- ;; FIXME: we used `newline' down here which called
- ;; self-insert-command and ran post-self-insert-hook recursively.
- ;; It happened to make electric-indent-mode work automatically with
- ;; electric-layout-mode (at the cost of re-indenting lines
- ;; multiple times), but I'm not sure it's what we want.
- ;;
- ;; FIXME: check eolp before inserting \n?
- ('before (goto-char (1- pos)) (skip-chars-backward " \t")
- (unless (bolp) (insert "\n")))
- ('after (insert "\n"))
- ('after-stay (save-excursion
- (let ((electric-layout-rules nil))
- (newline 1 t))))
- ('around (save-excursion
- (goto-char (1- pos)) (skip-chars-backward " \t")
- (unless (bolp) (insert "\n")))
- (insert "\n"))) ; FIXME: check eolp before inserting
\n?
- (goto-char end)))))
+ (goto-char pos)
+ (when (functionp rule) (setq rule (funcall rule)))
+ (dolist (sym (if (symbolp rule) (list rule) rule))
+ (let* ((nl-after
+ (lambda ()
+ ;; FIXME: we use `newline', which calls
+ ;; `self-insert-command' and ran
+ ;; `post-self-insert-hook' recursively. It
+ ;; happened to make `electric-indent-mode' work
+ ;; automatically with `electric-layout-mode' (at
+ ;; the cost of re-indenting lines multiple times),
+ ;; but I'm not sure it's what we want.
+ ;;
+ ;; FIXME: when `newline'ing, we exceptionally
+ ;; prevent a specific behaviour of
+ ;; `eletric-pair-mode', that of opening an extra
+ ;; newline between newly inserted matching paris.
+ ;; In theory that behaviour should be provided by
+ ;; `electric-layout-mode' instead, which should be
+ ;; possible given the current API.
+ ;;
+ ;; FIXME: check eolp before inserting \n?
+ (let ((electric-layout-mode nil)
+ (electric-pair-open-newline-between-pairs nil))
+ (newline 1 t))))
+ (nl-before (lambda ()
+ (save-excursion
+ (goto-char (1- pos)) (skip-chars-backward "
\t")
+ (unless (bolp) (funcall nl-after))))))
+ (pcase sym
+ ('before (funcall nl-before))
+ ('after (funcall nl-after))
+ ('after-stay (save-excursion (funcall nl-after)))
+ ('around (funcall nl-before) (funcall nl-after))))))))
(put 'electric-layout-post-self-insert-function 'priority 40)
@@ -419,6 +465,19 @@ The variable `electric-layout-rules' says when and how to
insert newlines."
(remove-hook 'post-self-insert-hook
#'electric-layout-post-self-insert-function))))
+;;;###autoload
+(define-minor-mode electric-layout-local-mode
+ "Toggle `electric-layout-mode' only in this buffer."
+ :variable (buffer-local-value 'electric-layout-mode (current-buffer))
+ (cond
+ ((eq electric-layout-mode (default-value 'electric-layout-mode))
+ (kill-local-variable 'electric-layout-mode))
+ ((not (default-value 'electric-layout-mode))
+ ;; Locally enabled, but globally disabled.
+ (electric-layout-mode 1) ; Setup the hooks.
+ (setq-default electric-layout-mode nil) ; But keep it globally disabled.
+ )))
+
;;; Electric quoting.
(defcustom electric-quote-comment t
diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el
index 467abd1..5a4b20e 100644
--- a/test/lisp/electric-tests.el
+++ b/test/lisp/electric-tests.el
@@ -812,5 +812,109 @@ baz\"\""
:bindings '((comment-start . "<!--") (comment-use-syntax . t))
:test-in-comments nil :test-in-strings nil)
+
+;;; tests for `electric-layout-mode'
+
+(ert-deftest electric-layout-int-main-kernel-style ()
+ (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 ()
+ (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/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 ()
+ (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}"))))
+
+(ert-deftest electric-pair-mode-newline-between-parens ()
+ (ert-with-test-buffer ()
+ (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 ?
))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main () {\n \n}"))))
+
+(ert-deftest electric-layout-mode-newline-between-parens-without-e-p-m ()
+ (ert-with-test-buffer ()
+ (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 ?
))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main () {\n \n}"))))
+
+(ert-deftest electric-layout-mode-newline-between-parens-without-e-p-m-2 ()
+ (ert-with-test-buffer ()
+ (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
+ '((lambda (char)
+ (when (and
+ (eq char ?\n)
+ (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 ?
))
+ (call-interactively (key-binding `[,last-command-event])))
+ (should (equal (buffer-string) "int main () {\n \n}"))))
+
(provide 'electric-tests)
;;; electric-tests.el ends here
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Emacs-diffs] master 949295a: Extend electric-layout-mode to handle more complex layouts (bug#33794),
João Távora <=