X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/0756b0c3a6e60dd623da6f12bc8d2de05ef7b8f1..2d809ffafd3e1bde360c08f3be4b7d9a43ff5f0e:/lisp/electric.el diff --git a/lisp/electric.el b/lisp/electric.el index 3d7c1fd8ac..86997d4aac 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -1,6 +1,7 @@ ;;; electric.el --- window maker and Command loop for `electric' modes -;; Copyright (C) 1985-1986, 1995, 2001-2011 Free Software Foundation, Inc. +;; Copyright (C) 1985-1986, 1995, 2001-2013 Free Software Foundation, +;; Inc. ;; Author: K. Shane Hartman ;; Maintainer: FSF @@ -38,8 +39,6 @@ ;;; Code: -(eval-when-compile (require 'cl)) - ;; This loop is the guts for non-standard modes which retain control ;; until some event occurs. It is a `do-forever', the only way out is ;; to throw. It assumes that you have set up the keymap, window, and @@ -85,7 +84,7 @@ (eq last-input-event ?\C-g)) (progn (setq unread-command-events nil prefix-arg nil) - ;; If it wasn't cancelling a prefix character, then quit. + ;; If it wasn't canceling a prefix character, then quit. (if (or (= (length (this-command-keys)) 1) (not inhibit-quit)) ; safety (progn (ding) @@ -199,6 +198,12 @@ Returns nil when we can't find this char." (defvar electric-indent-chars '(?\n) "Characters that should cause automatic reindentation.") +(defvar electric-indent-functions nil + "Special hook run to decide whether to auto-indent. +Each function is called with one argument (the inserted char), with +point right after that char, and it should return t to cause indentation, +`no-indent' to prevent indentation or nil to let other functions decide.") + (defun electric-indent-post-self-insert-function () ;; FIXME: This reindents the current line, but what we really want instead is ;; to reindent the whole affected text. That's the current line for simple @@ -208,13 +213,22 @@ Returns nil when we can't find this char." ;; There might be a way to get it working by analyzing buffer-undo-list, but ;; it looks challenging. (let (pos) - (when (and (memq last-command-event electric-indent-chars) - ;; Don't reindent while inserting spaces at beginning of line. - (or (not (memq last-command-event '(?\s ?\t))) - (save-excursion (skip-chars-backward " \t") (not (bolp)))) - (setq pos (electric--after-char-pos)) - ;; Not in a string or comment. - (not (nth 8 (save-excursion (syntax-ppss pos))))) + (when (and + electric-indent-mode + ;; Don't reindent while inserting spaces at beginning of line. + (or (not (memq last-command-event '(?\s ?\t))) + (save-excursion (skip-chars-backward " \t") (not (bolp)))) + (setq pos (electric--after-char-pos)) + (save-excursion + (goto-char pos) + (let ((act (or (run-hook-with-args-until-success + 'electric-indent-functions + last-command-event) + (memq last-command-event electric-indent-chars)))) + (not + (or (memq act '(nil no-indent)) + ;; In a string or comment. + (unless (eq act 'do-indent) (nth 8 (syntax-ppss)))))))) ;; For newline, we want to reindent both lines and basically behave like ;; reindent-then-newline-and-indent (whose code we hence copied). (when (< (1- pos) (line-beginning-position)) @@ -222,7 +236,7 @@ Returns nil when we can't find this char." (save-excursion (unless (memq indent-line-function '(indent-relative indent-to-left-margin - indent-relative-maybe)) + indent-relative-maybe)) ;; Don't reindent the previous line if the indentation function ;; is not a real one. (goto-char before) @@ -246,32 +260,36 @@ With a prefix argument ARG, enable Electric Indent mode if ARG is positive, and disable it otherwise. If called from Lisp, enable the mode if ARG is omitted or nil. -Electric Indent mode is a global minor mode. When enabled, -reindentation is triggered whenever you insert a character listed -in `electric-indent-chars'." +This is a global minor mode. When enabled, it reindents whenever +the hook `electric-indent-functions' returns non-nil, or you +insert a character from `electric-indent-chars'." :global t :group 'electricity - (if electric-indent-mode - (add-hook 'post-self-insert-hook - #'electric-indent-post-self-insert-function) - (remove-hook 'post-self-insert-hook - #'electric-indent-post-self-insert-function)) - ;; FIXME: electric-indent-mode and electric-layout-mode interact - ;; in non-trivial ways. It turns out that electric-indent-mode works - ;; better if it is run *after* electric-layout-mode's hook. - (when (memq #'electric-layout-post-self-insert-function - (memq #'electric-indent-post-self-insert-function - (default-value 'post-self-insert-hook))) - (remove-hook 'post-self-insert-hook - #'electric-layout-post-self-insert-function) + (if (not electric-indent-mode) + (remove-hook 'post-self-insert-hook + #'electric-indent-post-self-insert-function) + ;; post-self-insert-hooks interact in non-trivial ways. + ;; It turns out that electric-indent-mode generally works better if run + ;; late, but still before blink-paren. (add-hook 'post-self-insert-hook - #'electric-layout-post-self-insert-function))) + #'electric-indent-post-self-insert-function + 'append) + ;; FIXME: Ugly! + (let ((bp (memq #'blink-paren-post-self-insert-function + (default-value 'post-self-insert-hook)))) + (when (memq #'electric-indent-post-self-insert-function bp) + (setcar bp #'electric-indent-post-self-insert-function) + (setcdr bp (cons #'blink-paren-post-self-insert-function + (delq #'electric-indent-post-self-insert-function + (cdr bp)))))))) ;; Electric pairing. (defcustom electric-pair-pairs '((?\" . ?\")) "Alist of pairs that should be used regardless of major mode." + :group 'electricity + :version "24.1" :type '(repeat (cons character character))) (defcustom electric-pair-skip-self t @@ -280,16 +298,42 @@ When inserting a closing paren character right before the same character, just skip that character instead, so that hitting ( followed by ) results in \"()\" rather than \"())\". This can be convenient for people who find it easier to hit ) than C-f." + :group 'electricity + :version "24.1" :type 'boolean) +(defcustom electric-pair-inhibit-predicate + #'electric-pair-default-inhibit + "Predicate to prevent insertion of a matching pair. +The function is called with a single char (the opening char just inserted). +If it returns non-nil, then `electric-pair-mode' will not insert a matching +closer." + :version "24.4" + :type '(choice + (const :tag "Default" electric-pair-default-inhibit) + (const :tag "Always pair" ignore) + function)) + +(defun electric-pair-default-inhibit (char) + (or + ;; I find it more often preferable not to pair when the + ;; same char is next. + (eq char (char-after)) + (eq char (char-before (1- (point)))) + ;; I also find it often preferable not to pair next to a word. + (eq (char-syntax (following-char)) ?w))) + +(defun electric-pair-syntax (command-event) + (and electric-pair-mode + (let ((x (assq command-event electric-pair-pairs))) + (cond + (x (if (eq (car x) (cdr x)) ?\" ?\()) + ((rassq command-event electric-pair-pairs) ?\)) + (t (char-syntax command-event)))))) + (defun electric-pair-post-self-insert-function () (let* ((syntax (and (eq (char-before) last-command-event) ; Sanity check. - electric-pair-mode - (let ((x (assq last-command-event electric-pair-pairs))) - (cond - (x (if (eq (car x) (cdr x)) ?\" ?\()) - ((rassq last-command-event electric-pair-pairs) ?\)) - (t (char-syntax last-command-event)))))) + (electric-pair-syntax last-command-event))) ;; FIXME: when inserting the closer, we should maybe use ;; self-insert-command, although it may prove tricky running ;; post-self-insert-hook recursively, and we wouldn't want to trigger @@ -303,12 +347,13 @@ This can be convenient for people who find it easier to hit ) than C-f." ((and (memq syntax '(?\( ?\" ?\$)) (use-region-p)) (if (> (mark) (point)) (goto-char (mark)) - ;; We already inserted the open-paren but at the end of the region, - ;; so we have to remove it and start over. - (delete-char -1) - (save-excursion + ;; We already inserted the open-paren but at the end of the + ;; region, so we have to remove it and start over. + (delete-char -1) + (save-excursion (goto-char (mark)) - (insert last-command-event))) + ;; Do not insert after `save-excursion' marker (Bug#11520). + (insert-before-markers last-command-event))) (insert closer)) ;; Backslash-escaped: no pairing, no skipping. ((save-excursion @@ -327,14 +372,13 @@ This can be convenient for people who find it easier to hit ) than C-f." ;; Insert matching pair. ((not (or (not (memq syntax `(?\( ?\" ?\$))) overwrite-mode - ;; I find it more often preferable not to pair when the - ;; same char is next. - (eq last-command-event (char-after)) - (eq last-command-event (char-before (1- (point)))) - ;; I also find it often preferable not to pair next to a word. - (eq (char-syntax (following-char)) ?w))) + (funcall electric-pair-inhibit-predicate last-command-event))) (save-excursion (insert closer)))))) +(defun electric-pair-will-use-region () + (and (use-region-p) + (memq (electric-pair-syntax last-command-event) '(?\( ?\" ?\$)))) + ;;;###autoload (define-minor-mode electric-pair-mode "Toggle automatic parens pairing (Electric Pair mode). @@ -344,14 +388,21 @@ the mode if ARG is omitted or nil. Electric Pair mode is a global minor mode. When enabled, typing an open parenthesis automatically inserts the corresponding -closing parenthesis. \(Likewise for brackets, etc.)" +closing parenthesis. \(Likewise for brackets, etc.) + +See options `electric-pair-pairs' and `electric-pair-skip-self'." :global t :group 'electricity (if electric-pair-mode - (add-hook 'post-self-insert-hook - #'electric-pair-post-self-insert-function) + (progn + (add-hook 'post-self-insert-hook + #'electric-pair-post-self-insert-function) + (add-hook 'self-insert-uses-region-functions + #'electric-pair-will-use-region)) (remove-hook 'post-self-insert-hook - #'electric-pair-post-self-insert-function))) + #'electric-pair-post-self-insert-function) + (remove-hook 'self-insert-uses-region-functions + #'electric-pair-will-use-region))) ;; Automatically add newlines after/before/around some chars. @@ -359,8 +410,8 @@ closing parenthesis. \(Likewise for brackets, etc.)" "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', or a function that returns -one of those symbols.") +and can be: nil, `before', `after', `around', or a function of no +arguments that returns one of those symbols.") (defun electric-layout-post-self-insert-function () (let* ((rule (cdr (assq last-command-event electric-layout-rules))) @@ -371,16 +422,16 @@ one of those symbols.") (not (nth 8 (save-excursion (syntax-ppss pos))))) (let ((end (copy-marker (point) t))) (goto-char pos) - (case (if (functionp rule) (funcall rule) rule) + (pcase (if (functionp rule) (funcall rule) rule) ;; 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. - (before (goto-char (1- pos)) (skip-chars-backward " \t") + (`before (goto-char (1- pos)) (skip-chars-backward " \t") (unless (bolp) (insert "\n"))) - (after (insert "\n")) ; FIXME: check eolp before inserting \n? - (around (save-excursion + (`after (insert "\n")) ; FIXME: check eolp before inserting \n? + (`around (save-excursion (goto-char (1- pos)) (skip-chars-backward " \t") (unless (bolp) (insert "\n"))) (insert "\n"))) ; FIXME: check eolp before inserting \n? @@ -388,7 +439,11 @@ one of those symbols.") ;;;###autoload (define-minor-mode electric-layout-mode - "Automatically insert newlines around some chars." + "Automatically insert newlines around some chars. +With a prefix argument ARG, enable Electric Layout mode if ARG is +positive, and disable it otherwise. If called from Lisp, enable +the mode if ARG is omitted or nil. +The variable `electric-layout-rules' says when and how to insert newlines." :global t :group 'electricity (if electric-layout-mode