diff options
| author | João Távora | 2018-12-21 18:00:08 +0000 |
|---|---|---|
| committer | João Távora | 2019-01-02 12:55:16 +0000 |
| commit | 949295ae1a8a79a181b2bf614b9c69849f2fd667 (patch) | |
| tree | 6c1570504f7f43a2c47f1651dae18323b229cdc7 /lisp | |
| parent | 0515b223c2158984e135e84be97c01d5b8d0ae75 (diff) | |
| download | emacs-949295ae1a8a79a181b2bf614b9c69849f2fd667.tar.gz emacs-949295ae1a8a79a181b2bf614b9c69849f2fd667.zip | |
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.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/electric.el | 125 |
1 files changed, 92 insertions, 33 deletions
diff --git a/lisp/electric.el b/lisp/electric.el index 36841bc6ccd..e7ebdf5739d 100644 --- a/lisp/electric.el +++ b/lisp/electric.el | |||
| @@ -363,45 +363,91 @@ use `electric-indent-local-mode'." | |||
| 363 | (defvar electric-layout-rules nil | 363 | (defvar electric-layout-rules nil |
| 364 | "List of rules saying where to automatically insert newlines. | 364 | "List of rules saying where to automatically insert newlines. |
| 365 | 365 | ||
| 366 | Each rule has the form (CHAR . WHERE) where CHAR is the char that | 366 | Each rule has the form (CHAR . WHERE), the rule matching if the |
| 367 | was just inserted and WHERE specifies where to insert newlines | 367 | character just inserted was CHAR. WHERE specifies where to |
| 368 | and can be: nil, `before', `after', `around', `after-stay', or a | 368 | insert newlines, and can be: |
| 369 | function of no arguments that returns one of those symbols. | ||
| 370 | 369 | ||
| 371 | The symbols specify where in relation to CHAR the newline | 370 | * one of the symbols `before', `after', `around', `after-stay', |
| 372 | character(s) should be inserted. `after-stay' means insert a | 371 | or nil. |
| 373 | newline after CHAR but stay in the same place.") | 372 | |
| 373 | * a list of the preceding symbols, processed in order of | ||
| 374 | appearance to insert multiple newlines; | ||
| 375 | |||
| 376 | * a function of no arguments that returns one of the previous | ||
| 377 | values. | ||
| 378 | |||
| 379 | Each symbol specifies where, in relation to the position POS of | ||
| 380 | the character inserted, the newline character(s) should be | ||
| 381 | inserted. `after-stay' means insert a newline after POS but stay | ||
| 382 | in the same place. | ||
| 383 | |||
| 384 | Instead of the (CHAR . WHERE) form, a rule can also be just a | ||
| 385 | function of a single argument, the character just inserted. It | ||
| 386 | should return a value compatible with WHERE if the rule matches, | ||
| 387 | or nil if it doesn't match. | ||
| 388 | |||
| 389 | If multiple rules match, only first one is executed.") | ||
| 374 | 390 | ||
| 375 | (defun electric-layout-post-self-insert-function () | 391 | (defun electric-layout-post-self-insert-function () |
| 376 | (let* ((rule (cdr (assq last-command-event electric-layout-rules))) | 392 | (when electric-layout-mode |
| 377 | pos) | 393 | (electric-layout-post-self-insert-function-1))) |
| 394 | |||
| 395 | ;; for edebug's sake, a separate function | ||
| 396 | (defun electric-layout-post-self-insert-function-1 () | ||
| 397 | (let* (pos | ||
| 398 | probe | ||
| 399 | (rules electric-layout-rules) | ||
| 400 | (rule | ||
| 401 | (catch 'done | ||
| 402 | (while (setq probe (pop rules)) | ||
| 403 | (cond ((and (consp probe) | ||
| 404 | (eq (car probe) last-command-event)) | ||
| 405 | (throw 'done (cdr probe))) | ||
| 406 | ((functionp probe) | ||
| 407 | (let ((res | ||
| 408 | (save-excursion | ||
| 409 | (goto-char | ||
| 410 | (or pos (setq pos (electric--after-char-pos)))) | ||
| 411 | (funcall probe last-command-event)))) | ||
| 412 | (when res (throw 'done res))))))))) | ||
| 378 | (when (and rule | 413 | (when (and rule |
| 379 | (setq pos (electric--after-char-pos)) | 414 | (or pos (setq pos (electric--after-char-pos))) |
| 380 | ;; Not in a string or comment. | 415 | ;; Not in a string or comment. |
| 381 | (not (nth 8 (save-excursion (syntax-ppss pos))))) | 416 | (not (nth 8 (save-excursion (syntax-ppss pos))))) |
| 382 | (let ((end (point-marker)) | 417 | (goto-char pos) |
| 383 | (sym (if (functionp rule) (funcall rule) rule))) | 418 | (when (functionp rule) (setq rule (funcall rule))) |
| 384 | (set-marker-insertion-type end (not (eq sym 'after-stay))) | 419 | (dolist (sym (if (symbolp rule) (list rule) rule)) |
| 385 | (goto-char pos) | 420 | (let* ((nl-after |
| 386 | (pcase sym | 421 | (lambda () |
| 387 | ;; FIXME: we used `newline' down here which called | 422 | ;; FIXME: we use `newline', which calls |
| 388 | ;; self-insert-command and ran post-self-insert-hook recursively. | 423 | ;; `self-insert-command' and ran |
| 389 | ;; It happened to make electric-indent-mode work automatically with | 424 | ;; `post-self-insert-hook' recursively. It |
| 390 | ;; electric-layout-mode (at the cost of re-indenting lines | 425 | ;; happened to make `electric-indent-mode' work |
| 391 | ;; multiple times), but I'm not sure it's what we want. | 426 | ;; automatically with `electric-layout-mode' (at |
| 392 | ;; | 427 | ;; the cost of re-indenting lines multiple times), |
| 393 | ;; FIXME: check eolp before inserting \n? | 428 | ;; but I'm not sure it's what we want. |
| 394 | ('before (goto-char (1- pos)) (skip-chars-backward " \t") | 429 | ;; |
| 395 | (unless (bolp) (insert "\n"))) | 430 | ;; FIXME: when `newline'ing, we exceptionally |
| 396 | ('after (insert "\n")) | 431 | ;; prevent a specific behaviour of |
| 397 | ('after-stay (save-excursion | 432 | ;; `eletric-pair-mode', that of opening an extra |
| 398 | (let ((electric-layout-rules nil)) | 433 | ;; newline between newly inserted matching paris. |
| 399 | (newline 1 t)))) | 434 | ;; In theory that behaviour should be provided by |
| 400 | ('around (save-excursion | 435 | ;; `electric-layout-mode' instead, which should be |
| 401 | (goto-char (1- pos)) (skip-chars-backward " \t") | 436 | ;; possible given the current API. |
| 402 | (unless (bolp) (insert "\n"))) | 437 | ;; |
| 403 | (insert "\n"))) ; FIXME: check eolp before inserting \n? | 438 | ;; FIXME: check eolp before inserting \n? |
| 404 | (goto-char end))))) | 439 | (let ((electric-layout-mode nil) |
| 440 | (electric-pair-open-newline-between-pairs nil)) | ||
| 441 | (newline 1 t)))) | ||
| 442 | (nl-before (lambda () | ||
| 443 | (save-excursion | ||
| 444 | (goto-char (1- pos)) (skip-chars-backward " \t") | ||
| 445 | (unless (bolp) (funcall nl-after)))))) | ||
| 446 | (pcase sym | ||
| 447 | ('before (funcall nl-before)) | ||
| 448 | ('after (funcall nl-after)) | ||
| 449 | ('after-stay (save-excursion (funcall nl-after))) | ||
| 450 | ('around (funcall nl-before) (funcall nl-after)))))))) | ||
| 405 | 451 | ||
| 406 | (put 'electric-layout-post-self-insert-function 'priority 40) | 452 | (put 'electric-layout-post-self-insert-function 'priority 40) |
| 407 | 453 | ||
| @@ -419,6 +465,19 @@ The variable `electric-layout-rules' says when and how to insert newlines." | |||
| 419 | (remove-hook 'post-self-insert-hook | 465 | (remove-hook 'post-self-insert-hook |
| 420 | #'electric-layout-post-self-insert-function)))) | 466 | #'electric-layout-post-self-insert-function)))) |
| 421 | 467 | ||
| 468 | ;;;###autoload | ||
| 469 | (define-minor-mode electric-layout-local-mode | ||
| 470 | "Toggle `electric-layout-mode' only in this buffer." | ||
| 471 | :variable (buffer-local-value 'electric-layout-mode (current-buffer)) | ||
| 472 | (cond | ||
| 473 | ((eq electric-layout-mode (default-value 'electric-layout-mode)) | ||
| 474 | (kill-local-variable 'electric-layout-mode)) | ||
| 475 | ((not (default-value 'electric-layout-mode)) | ||
| 476 | ;; Locally enabled, but globally disabled. | ||
| 477 | (electric-layout-mode 1) ; Setup the hooks. | ||
| 478 | (setq-default electric-layout-mode nil) ; But keep it globally disabled. | ||
| 479 | ))) | ||
| 480 | |||
| 422 | ;;; Electric quoting. | 481 | ;;; Electric quoting. |
| 423 | 482 | ||
| 424 | (defcustom electric-quote-comment t | 483 | (defcustom electric-quote-comment t |