From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Bozhidar Batsov Newsgroups: gmane.emacs.devel Subject: Re: [patch] make electric-pair-mode smarter/more useful Date: Tue, 24 Dec 2013 16:29:22 +0200 Message-ID: References: <87haalh806.fsf@gmail.com> <87d2l9wfne.fsf@yandex.ru> <87fvq49xzp.fsf@gmail.com> <87vbyuwyyc.fsf@gmail.com> <52A93B99.8040308@yandex.ru> <87r49if185.fsf@gmail.com> <52AA772D.7050503@yandex.ru> <52AC8DA6.5070403@yandex.ru> <87ppoxaaz4.fsf@gmail.com> <878uvi6fq6.fsf@gmail.com> <87y53irx7j.fsf@gmail.com> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary=089e015383e41aee8b04ee489231 X-Trace: ger.gmane.org 1387895380 7516 80.91.229.3 (24 Dec 2013 14:29:40 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 24 Dec 2013 14:29:40 +0000 (UTC) Cc: emacs-devel , Stefan Monnier , Dmitry Gutov To: =?UTF-8?B?Sm/Do28gVMOhdm9yYQ==?= Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Dec 24 15:29:47 2013 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1VvSzR-0003aH-Ts for ged-emacs-devel@m.gmane.org; Tue, 24 Dec 2013 15:29:46 +0100 Original-Received: from localhost ([::1]:38779 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VvSzR-00008U-FX for ged-emacs-devel@m.gmane.org; Tue, 24 Dec 2013 09:29:45 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:42499) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VvSzF-00008P-8n for emacs-devel@gnu.org; Tue, 24 Dec 2013 09:29:42 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VvSz6-0006ey-CX for emacs-devel@gnu.org; Tue, 24 Dec 2013 09:29:33 -0500 Original-Received: from mail-oa0-x232.google.com ([2607:f8b0:4003:c02::232]:54710) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VvSz5-0006ek-RJ for emacs-devel@gnu.org; Tue, 24 Dec 2013 09:29:24 -0500 Original-Received: by mail-oa0-f50.google.com with SMTP id n16so7080461oag.9 for ; Tue, 24 Dec 2013 06:29:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:sender:in-reply-to:references:date:message-id:subject :from:to:cc:content-type; bh=p8PWca7qIitLItGKOa3TS8l5D0O7U86OwUfSxaJLpWM=; b=DvbYQVtG0FAl7BPRF//XYlwPcPvw4IeUIgymX5WUkWymKXDPZI6gRLxA30wjktQ3pd 2YZjZBzonh457rWUvuic5K2gb+LGfICoHJn3lHw+STtWOwsIH2LFNzSz8Js2X9Y/1x4x M0HcI+CLmHVVDSJpoTUPyj6F6ChQQ3Jey9qWxI/BffWHPQ4F9FWYC/uOvNymTn59Um5c XSMIInp2ru3sVAiTszxe2Iv9+yVWNJ1c+CjKLWmCRLXLQ/sMsSb0jtgp8AVMMV5adEFY cjHlrtwfdSztC9sc0jM45GKgWYyUR7PS6C28U6B/ubGVmXkl1p7ynMCTADSsPbVA9u8a ZIfA== X-Received: by 10.182.196.3 with SMTP id ii3mr22104040obc.11.1387895363135; Tue, 24 Dec 2013 06:29:23 -0800 (PST) Original-Received: by 10.76.109.98 with HTTP; Tue, 24 Dec 2013 06:29:22 -0800 (PST) In-Reply-To: X-Google-Sender-Auth: l9JPJ4lJy8Wl-TsAYY4ckytg7pI X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:4003:c02::232 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:166827 Archived-At: --089e015383e41aee8b04ee489231 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Not sure if Stefan saw your message here, but he mentioned in the "feature freeze" thread that your patch is approved and can be installed. Have a look at it. On 23 December 2013 16:41, Jo=C3=A3o T=C3=A1vora wro= te: > > Stefan, > > Don't want to be pushy, but is there anything holding up this patch? > > Here's yet another version that fixes some more details: > > - fixed default values for `electric-pair-inhibit-predicate' and > `electric-pair-skip-self'. > > - electric-pair-mode's docstring doesn't mention related customization > variables because there are now too many of them. > > - emacs-lisp/lisp-mode.el locally sets > `electric-pair-open-newline-between-pairs' to t. > > - the newline call in `newline-and-indent` now uses interactive set to > t > > Thanks > Jo=C3=A3o > > diff --git a/lisp/ChangeLog b/lisp/ChangeLog > index 12889de..0564484 100644 > --- a/lisp/ChangeLog > +++ b/lisp/ChangeLog > @@ -1,3 +1,25 @@ > +2013-12-99 Jo=C3=83=C2=A3o T=C3=83=C2=A1vora > + > + * electric.el (electric-pair-mode): More flexible engine for skip= - > + and inhibit predicates, new options for pairing-related > + functionality. > + (electric-pair-preserve-balance): Pair/skip parentheses and quote= s > + if that keeps or improves their balance in buffers. > + (electric-pair-delete-adjacent-pairs): Delete the pair when > + backspacing over adjacent matched delimiters. > + (electric-pair-open-extra-newline): Open extra newline when > + inserting newlines between adjacent matched delimiters. > + (electric--sort-post-self-insertion-hook): Sort > + post-self-insert-hook according to priority values when > + minor-modes are activated. > + * simple.el (newline-and-indent): Call newline with interactive > + set to t. > + (blink-paren-post-self-insert-function): Set priority to 100. > + * emacs-lisp/lisp-mode.el (lisp-mode-variables): Use > + electric-pair-text-pairs to pair backtick-and-quote in strings an= d > + comments. Locally set electric-pair-skip-whitespace to 'chomp and > + electric-pair-open-newline-between-pairs to nil. > + > 2013-12-23 Chong Yidong > > * subr.el (set-transient-map): Rename from > diff --git a/lisp/electric.el b/lisp/electric.el > index 91b99b4..ca4616b 100644 > --- a/lisp/electric.el > +++ b/lisp/electric.el > @@ -187,6 +187,17 @@ Returns nil when we can't find this char." > (eq (char-before) last-command-event))))) > pos))) > > +(defun electric--sort-post-self-insertion-hook () > + "Ensure order of electric functions in `post-self-insertion-hook'. > + > +Hooks in this variable interact in non-trivial ways, so a > +relative order must be maintained within it." > + (setq-default post-self-insert-hook > + (sort (default-value 'post-self-insert-hook) > + #'(lambda (fn1 fn2) > + (< (or (get fn1 'priority) 0) > + (or (get fn2 'priority) 0)))))) > + > ;;; Electric indentation. > > ;; Autoloading variables is generally undesirable, but major modes > @@ -267,6 +278,8 @@ mode set `electric-indent-inhibit', but this can be > used as a workaround.") > (> pos (line-beginning-position))) > (indent-according-to-mode))))) > > +(put 'electric-indent-post-self-insert-function 'priority 60) > + > (defun electric-indent-just-newline (arg) > "Insert just a newline, without any auto-indentation." > (interactive "*P") > @@ -295,20 +308,9 @@ insert a character from `electric-indent-chars'." > #'electric-indent-post-self-insert-function)) > (when (eq (lookup-key global-map [?\C-j]) 'newline-and-indent) > (define-key global-map [?\C-j] 'electric-indent-just-newline)) > - ;; 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-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-functi= on > - (cdr bp)))))))) > + #'electric-indent-post-self-insert-function) > + (electric--sort-post-self-insertion-hook))) > > ;;;###autoload > (define-minor-mode electric-indent-local-mode > @@ -327,32 +329,163 @@ insert a character from `electric-indent-chars'." > > (defcustom electric-pair-pairs > '((?\" . ?\")) > - "Alist of pairs that should be used regardless of major mode." > + "Alist of pairs that should be used regardless of major mode. > + > +Pairs of delimiters in this list are a fallback in case they have > +no syntax relevant to `electric-pair-mode' in the mode's syntax > +table. > + > +See also the variable `electric-pair-text-pairs'." > :version "24.1" > :type '(repeat (cons character character))) > > -(defcustom electric-pair-skip-self t > +(defcustom electric-pair-text-pairs > + '((?\" . ?\" )) > + "Alist of pairs that should always be used in comments and strings. > + > +Pairs of delimiters in this list are a fallback in case they have > +no syntax relevant to `electric-pair-mode' in the syntax table > +defined in `electric-pair-text-syntax-table'" > + :version "24.4" > + :type '(repeat (cons character character))) > + > +(defcustom electric-pair-skip-self #'electric-pair-default-skip-self > "If non-nil, skip char instead of inserting a second closing paren. > + > When inserting a closing paren character right before the same character= , > just skip that character instead, so that hitting ( followed by ) result= s > in \"()\" rather than \"())\". > -This can be convenient for people who find it easier to hit ) than C-f." > + > +This can be convenient for people who find it easier to hit ) than C-f. > + > +Can also be a function of one argument (the closer char just > +inserted), in which case that function's return value is > +considered instead." > :version "24.1" > - :type 'boolean) > + :type '(choice > + (const :tag "Never skip" nil) > + (const :tag "Help balance" electric-pair-default-skip-self) > + (const :tag "Always skip" t) > + function)) > > (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 "Conservative" electric-pair-conservative-inhibit) > + (const :tag "Help balance" electric-pair-default-inhibit) > (const :tag "Always pair" ignore) > function)) > > -(defun electric-pair-default-inhibit (char) > +(defcustom electric-pair-preserve-balance t > + "Non-nil if default pairing and skipping should help balance > parentheses. > + > +The default values of `electric-pair-inhibit-predicate' and > +`electric-pair-skip-self' check this variable before delegating to other > +predicates reponsible for making decisions on whether to pair/skip some > +characters based on the actual state of the buffer's parenthesis and > +quotes." > + :version "24.4" > + :type 'boolean) > + > +(defcustom electric-pair-delete-adjacent-pairs t > + "If non-nil, backspacing an open paren also deletes adjacent closer. > + > +Can also be a function of no arguments, in which case that function's > +return value is considered instead." > + :version "24.4" > + :type '(choice > + (const :tag "Yes" t) > + (const :tag "No" nil) > + function)) > + > +(defcustom electric-pair-open-newline-between-pairs t > + "If non-nil, a newline between adjacent parentheses opens an extra one= . > + > +Can also be a function of no arguments, in which case that function's > +return value is considered instead." > + :version "24.4" > + :type '(choice > + (const :tag "Yes" t) > + (const :tag "No" nil) > + function)) > + > +(defcustom electric-pair-skip-whitespace t > + "If non-nil skip whitespace when skipping over closing parens. > + > +The specific kind of whitespace skipped is given by the variable > +`electric-pair-skip-whitespace-chars'. > + > +The symbol `chomp' specifies that the skipped-over whitespace > +should be deleted. > + > +Can also be a function of no arguments, in which case that function's > +return value is considered instead." > + :version "24.4" > + :type '(choice > + (const :tag "Yes, jump over whitespace" t) > + (const :tag "Yes, and delete whitespace" 'chomp) > + (const :tag "No, no whitespace skipping" nil) > + function)) > + > +(defcustom electric-pair-skip-whitespace-chars (list ?\t ?\s ?\n) > + "Whitespace characters considered by `electric-pair-skip-whitespace'." > + :version "24.4" > + :type '(choice (set (const :tag "Space" ?\s) > + (const :tag "Tab" ?\t) > + (const :tag "Newline" ?\n)) > + (list character))) > + > +(defun electric-pair--skip-whitespace () > + "Skip whitespace forward, not crossing comment or string boundaries." > + (let ((saved (point)) > + (string-or-comment (nth 8 (syntax-ppss)))) > + (skip-chars-forward (apply #'string > electric-pair-skip-whitespace-chars)) > + (unless (eq string-or-comment (nth 8 (syntax-ppss))) > + (goto-char saved)))) > + > +(defvar electric-pair-text-syntax-table prog-mode-syntax-table > + "Syntax table used when pairing inside comments and strings. > + > +`electric-pair-mode' considers this syntax table only when point in insi= de > +quotes or comments. If lookup fails here, `electric-pair-text-pairs' wil= l > +be considered.") > + > +(defun electric-pair-backward-delete-char (n &optional killflag untabify= ) > + "Delete characters backward, and maybe also two adjacent paired > delimiters. > + > +Remaining behaviour is given by `backward-delete-char' or, if UNTABIFY i= s > +non-nil, `backward-delete-char-untabify'." > + (interactive "*p\nP") > + (let* ((prev (char-before)) > + (next (char-after)) > + (syntax-info (electric-pair-syntax-info prev)) > + (syntax (car syntax-info)) > + (pair (cadr syntax-info))) > + (when (and (if (functionp electric-pair-delete-adjacent-pairs) > + (funcall electric-pair-delete-adjacent-pairs) > + electric-pair-delete-adjacent-pairs) > + next > + (memq syntax '(?\( ?\" ?\$)) > + (eq pair next)) > + (delete-char 1 killflag)) > + (if untabify > + (backward-delete-char-untabify n killflag) > + (backward-delete-char n killflag)))) > + > +(defun electric-pair-backward-delete-char-untabify (n &optional killflag= ) > + "Delete characters backward, and maybe also two adjacent paired > delimiters. > + > +Remaining behaviour is given by `backward-delete-char-untabify'." > + (interactive "*p\nP") > + (electric-pair-backward-delete-char n killflag t)) > + > +(defun electric-pair-conservative-inhibit (char) > (or > ;; I find it more often preferable not to pair when the > ;; same char is next. > @@ -363,14 +496,40 @@ closer." > ;; 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) > - (let ((x (assq command-event electric-pair-pairs))) > +(defun electric-pair-syntax-info (command-event) > + "Calculate a list (SYNTAX PAIR UNCONDITIONAL STRING-OR-COMMENT-START). > + > +SYNTAX is COMMAND-EVENT's syntax character. PAIR is > +COMMAND-EVENT's pair. UNCONDITIONAL indicates the variables > +`electric-pair-pairs' or `electric-pair-text-pairs' were used to > +lookup syntax. STRING-OR-COMMENT-START indicates that point is > +inside a comment of string." > + (let* ((pre-string-or-comment (nth 8 (save-excursion > + (syntax-ppss (1- (point)))))) > + (post-string-or-comment (nth 8 (syntax-ppss (point)))) > + (string-or-comment (and post-string-or-comment > + pre-string-or-comment)) > + (table (if string-or-comment > + electric-pair-text-syntax-table > + (syntax-table))) > + (table-syntax-and-pair (with-syntax-table table > + (list (char-syntax command-event) > + (or (matching-paren command-even= t) > + command-event)))) > + (fallback (if string-or-comment > + (append electric-pair-text-pairs > + electric-pair-pairs) > + electric-pair-pairs)) > + (direct (assq command-event fallback)) > + (reverse (rassq command-event fallback))) > (cond > - (x (if (eq (car x) (cdr x)) ?\" ?\()) > - ((rassq command-event electric-pair-pairs) ?\)) > - ((nth 8 (syntax-ppss)) > - (with-syntax-table text-mode-syntax-table (char-syntax > command-event))) > - (t (char-syntax command-event))))) > + ((memq (car table-syntax-and-pair) > + '(?\" ?\( ?\) ?\$)) > + (append table-syntax-and-pair (list nil string-or-comment))) > + (direct (if (eq (car direct) (cdr direct)) > + (list ?\" command-event t string-or-comment) > + (list ?\( (cdr direct) t string-or-comment))) > + (reverse (list ?\) (car reverse) t string-or-comment))))) > > (defun electric-pair--insert (char) > (let ((last-command-event char) > @@ -378,56 +537,286 @@ closer." > (electric-pair-mode nil)) > (self-insert-command 1))) > > +(defun electric-pair--syntax-ppss (&optional pos where) > + "Like `syntax-ppss', but sometimes fallback to `parse-partial-sexp'. > + > +WHERE is list defaulting to '(string comment) and indicates > +when to fallback to `parse-partial-sexp'." > + (let* ((pos (or pos (point))) > + (where (or where '(string comment))) > + (quick-ppss (syntax-ppss)) > + (quick-ppss-at-pos (syntax-ppss pos))) > + (if (or (and (nth 3 quick-ppss) (memq 'string where)) > + (and (nth 4 quick-ppss) (memq 'comment where))) > + (with-syntax-table electric-pair-text-syntax-table > + (parse-partial-sexp (1+ (nth 8 quick-ppss)) pos)) > + ;; HACK! cc-mode apparently has some `syntax-ppss' bugs > + (if (memq major-mode '(c-mode c++ mode)) > + (parse-partial-sexp (point-min) pos) > + quick-ppss-at-pos)))) > + > +;; Balancing means controlling pairing and skipping of parentheses so > +;; that, if possible, the buffer ends up at least as balanced as > +;; before, if not more. The algorithm is slightly complex because some > +;; situations like "()))" need pairing to occur at the end but not at > +;; the beginning. Balancing should also happen independently for > +;; different types of parentheses, so that having your {}'s unbalanced > +;; doesn't keep `electric-pair-mode' from balancing your ()'s and your > +;; []'s. > +(defun electric-pair--balance-info (direction string-or-comment) > + "Examine lists forward or backward according to DIRECTIONS's sign. > + > +STRING-OR-COMMENT is info suitable for running `parse-partial-sexp'. > + > +Return a cons of two descritions (MATCHED-P . PAIR) for the > +innermost and outermost lists that enclose point. The outermost > +list enclosing point is either the first top-level or first > +mismatched list found by uplisting. > + > +If the outermost list is matched, don't rely on its PAIR. If > +point is not enclosed by any lists, return ((T) (T))." > + (let* (innermost > + outermost > + (table (if string-or-comment > + electric-pair-text-syntax-table > + (syntax-table))) > + (at-top-level-or-equivalent-fn > + ;; called when `scan-sexps' ran perfectly, when when it > + ;; found a parenthesis pointing in the direction of > + ;; travel. Also when travel started inside a comment and > + ;; exited it > + #'(lambda () > + (setq outermost (list t)) > + (unless innermost > + (setq innermost (list t))))) > + (ended-prematurely-fn > + ;; called when `scan-sexps' crashed against a parenthesis > + ;; pointing opposite the direction of travel. After > + ;; traversing that character, the idea is to travel one sexp > + ;; in the opposite direction looking for a matching > + ;; delimiter. > + #'(lambda () > + (let* ((pos (point)) > + (matched > + (save-excursion > + (cond ((< direction 0) > + (condition-case nil > + (eq (char-after pos) > + (with-syntax-table table > + (matching-paren > + (char-before > + (scan-sexps (point) 1))))) > + (scan-error nil))) > + (t > + ;; In this case, no need to use > + ;; `scan-sexps', we can use some > + ;; `electric-pair--syntax-ppss' in this > + ;; case (which uses the quicker > + ;; `syntax-ppss' in some cases) > + (let* ((ppss (electric-pair--syntax-ppss > + (1- (point)))) > + (start (car (last (nth 9 ppss)))) > + (opener (char-after start))) > + (and start > + (eq (char-before pos) > + (or (with-syntax-table table > + (matching-paren opener)) > + opener)))))))) > + (actual-pair (if (> direction 0) > + (char-before (point)) > + (char-after (point))))) > + (unless innermost > + (setq innermost (cons matched actual-pair))) > + (unless matched > + (setq outermost (cons matched actual-pair))))))) > + (save-excursion > + (while (not outermost) > + (condition-case err > + (with-syntax-table table > + (scan-sexps (point) (if (> direction 0) > + (point-max) > + (- (point-max)))) > + (funcall at-top-level-or-equivalent-fn)) > + (scan-error > + (cond ((or > + ;; some error happened and it is not of the "ended > + ;; prematurely" kind"... > + (not (string-match "ends prematurely" (nth 1 err))) > + ;; ... or we were in a comment and just came out of > + ;; it. > + (and string-or-comment > + (not (nth 8 (syntax-ppss))))) > + (funcall at-top-level-or-equivalent-fn)) > + (t > + ;; exit the sexp > + (goto-char (nth 3 err)) > + (funcall ended-prematurely-fn))))))) > + (cons innermost outermost))) > + > +(defun electric-pair--looking-at-unterminated-string-p (char) > + "Say if following string starts with CHAR and is unterminated." > + ;; FIXME: ugly/naive > + (save-excursion > + (skip-chars-forward (format "^%c" char)) > + (while (not (zerop (% (save-excursion (skip-syntax-backward "\\")) > 2))) > + (unless (eobp) > + (forward-char 1) > + (skip-chars-forward (format "^%c" char)))) > + (and (not (eobp)) > + (condition-case err > + (progn (forward-sexp) nil) > + (scan-error t))))) > + > +(defun electric-pair--inside-string-p (char) > + "Say if point is inside a string started by CHAR. > + > +A comments text is parsed with `electric-pair-text-syntax-table'. > +Also consider strings within comments, but not strings within > +strings." > + ;; FIXME: could also consider strings within strings by examining > + ;; delimiters. > + (let* ((ppss (electric-pair--syntax-ppss (point) '(comment)))) > + (memq (nth 3 ppss) (list t char)))) > + > +(defun electric-pair-inhibit-if-helps-balance (char) > + "Return non-nil if auto-pairing of CHAR would hurt parentheses' balanc= e. > + > +Works by first removing the character from the buffer, then doing > +some list calculations, finally restoring the situation as if nothing > +happened." > + (pcase (electric-pair-syntax-info char) > + (`(,syntax ,pair ,_ ,s-or-c) > + (unwind-protect > + (progn > + (delete-char -1) > + (cond ((eq ?\( syntax) > + (let* ((pair-data > + (electric-pair--balance-info 1 s-or-c)) > + (innermost (car pair-data)) > + (outermost (cdr pair-data))) > + (cond ((car outermost) > + nil) > + (t > + (eq (cdr outermost) pair))))) > + ((eq syntax ?\") > + (electric-pair--looking-at-unterminated-string-p > char)))) > + (insert-char char))))) > + > +(defun electric-pair-skip-if-helps-balance (char) > + "Return non-nil if skipping CHAR would benefit parentheses' balance. > + > +Works by first removing the character from the buffer, then doing > +some list calculations, finally restoring the situation as if nothing > +happened." > + (pcase (electric-pair-syntax-info char) > + (`(,syntax ,pair ,_ ,s-or-c) > + (unwind-protect > + (progn > + (delete-char -1) > + (cond ((eq syntax ?\)) > + (let* ((pair-data > + (electric-pair--balance-info > + -1 s-or-c)) > + (innermost (car pair-data)) > + (outermost (cdr pair-data))) > + (and > + (cond ((car outermost) > + (car innermost)) > + ((car innermost) > + (not (eq (cdr outermost) pair))))))) > + ((eq syntax ?\") > + (electric-pair--inside-string-p char)))) > + (insert-char char))))) > + > +(defun electric-pair-default-skip-self (char) > + (if electric-pair-preserve-balance > + (electric-pair-skip-if-helps-balance char) > + t)) > + > +(defun electric-pair-default-inhibit (char) > + (if electric-pair-preserve-balance > + (electric-pair-inhibit-if-helps-balance char) > + (electric-pair-conservative-inhibit char))) > + > (defun electric-pair-post-self-insert-function () > (let* ((pos (and electric-pair-mode (electric--after-char-pos))) > - (syntax (and pos (electric-pair-syntax last-command-event))) > - (closer (if (eq syntax ?\() > - (cdr (or (assq last-command-event > electric-pair-pairs) > - (aref (syntax-table) last-command-event))) > - last-command-event))) > - (cond > - ((null pos) nil) > - ;; Wrap a pair around the active region. > - ((and (memq syntax '(?\( ?\" ?\$)) (use-region-p)) > - ;; FIXME: To do this right, we'd need a post-self-insert-function > - ;; so we could add-function around it and insert the closer after > - ;; all the rest of the hook has run. > - (if (>=3D (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-region (1- pos) (point)) > - (save-excursion > - (goto-char (mark)) > - (electric-pair--insert last-command-event))) > - ;; Since we're right after the closer now, we could tell the rest = of > - ;; post-self-insert-hook that we inserted `closer', but then we'd > get > - ;; blink-paren to kick in, which is annoying. > - ;;(setq last-command-event closer) > - (insert closer)) > - ;; Backslash-escaped: no pairing, no skipping. > - ((save-excursion > - (goto-char (1- pos)) > - (not (zerop (% (skip-syntax-backward "\\") 2)))) > - nil) > - ;; Skip self. > - ((and (memq syntax '(?\) ?\" ?\$)) > - electric-pair-skip-self > - (eq (char-after pos) last-command-event)) > - ;; This is too late: rather than insert&delete we'd want to only > skip (or > - ;; insert in overwrite mode). The difference is in what goes in t= he > - ;; undo-log and in the intermediate state which might be visible t= o > other > - ;; post-self-insert-hook. We'll just have to live with it for now= . > - (delete-char 1)) > - ;; Insert matching pair. > - ((not (or (not (memq syntax `(?\( ?\" ?\$))) > - overwrite-mode > - (funcall electric-pair-inhibit-predicate > last-command-event))) > - (save-excursion (electric-pair--insert closer)))))) > + (skip-whitespace-info)) > + (pcase (electric-pair-syntax-info last-command-event) > + (`(,syntax ,pair ,unconditional ,_) > + (cond > + ((null pos) nil) > + ;; Wrap a pair around the active region. > + ;; > + ((and (memq syntax '(?\( ?\) ?\" ?\$)) (use-region-p)) > + ;; FIXME: To do this right, we'd need a post-self-insert-functi= on > + ;; so we could add-function around it and insert the closer aft= er > + ;; all the rest of the hook has run. > + (if (or (eq syntax ?\") > + (and (eq syntax ?\)) > + (>=3D (point) (mark))) > + (and (not (eq syntax ?\))) > + (>=3D (mark) (point)))) > + (save-excursion > + (goto-char (mark)) > + (electric-pair--insert pair)) > + (delete-region pos (1- pos)) > + (electric-pair--insert pair) > + (goto-char (mark)) > + (electric-pair--insert last-command-event))) > + ;; Backslash-escaped: no pairing, no skipping. > + ((save-excursion > + (goto-char (1- pos)) > + (not (zerop (% (skip-syntax-backward "\\") 2)))) > + nil) > + ;; Skip self. > + ((and (memq syntax '(?\) ?\" ?\$)) > + (and (or unconditional > + (if (functionp electric-pair-skip-self) > + (funcall electric-pair-skip-self > last-command-event) > + electric-pair-skip-self)) > + (save-excursion > + (when (setq skip-whitespace-info > + (if (functionp > electric-pair-skip-whitespace) > + (funcall > electric-pair-skip-whitespace) > + electric-pair-skip-whitespace)) > + (electric-pair--skip-whitespace)) > + (eq (char-after) last-command-event)))) > + ;; This is too late: rather than insert&delete we'd want to onl= y > + ;; skip (or insert in overwrite mode). The difference is in wh= at > + ;; goes in the undo-log and in the intermediate state which mig= ht > + ;; be visible to other post-self-insert-hook. We'll just have = to > + ;; live with it for now. > + (when skip-whitespace-info > + (electric-pair--skip-whitespace)) > + (delete-region (1- pos) (if (eq skip-whitespace-info 'chomp) > + (point) > + pos)) > + (forward-char)) > + ;; Insert matching pair. > + ((and (memq syntax `(?\( ?\" ?\$)) > + (not overwrite-mode) > + (or unconditional > + (not (funcall electric-pair-inhibit-predicate > + last-command-event)))) > + (save-excursion (electric-pair--insert pair))))) > + (t > + (when (and (if (functionp electric-pair-open-newline-between-pair= s) > + (funcall electric-pair-open-newline-between-pairs) > + electric-pair-open-newline-between-pairs) > + (eq last-command-event ?\n) > + (not (eobp)) > + (eq (save-excursion > + (skip-chars-backward "\t\s") > + (char-before (1- (point)))) > + (matching-paren (char-after)))) > + (save-excursion (newline 1 t))))))) > + > +(put 'electric-pair-post-self-insert-function 'priority 20) > > (defun electric-pair-will-use-region () > (and (use-region-p) > - (memq (electric-pair-syntax last-command-event) '(?\( ?\" ?\$)))) > + (memq (car (electric-pair-syntax-info last-command-event)) > + '(?\( ?\) ?\" ?\$)))) > > ;;;###autoload > (define-minor-mode electric-pair-mode > @@ -438,29 +827,44 @@ 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.) > - > -See options `electric-pair-pairs' and `electric-pair-skip-self'." > +closing parenthesis. \(Likewise for brackets, etc.)." > :global t :group 'electricity > (if electric-pair-mode > (progn > (add-hook 'post-self-insert-hook > #'electric-pair-post-self-insert-function) > + (electric--sort-post-self-insertion-hook) > (add-hook 'self-insert-uses-region-functions > #'electric-pair-will-use-region)) > (remove-hook 'post-self-insert-hook > #'electric-pair-post-self-insert-function) > (remove-hook 'self-insert-uses-region-functions > - #'electric-pair-will-use-region))) > + #'electric-pair-will-use-region))) > + > +(defvar electric-pair-mode-map > + (let ((map (make-sparse-keymap))) > + (define-key map [remap backward-delete-char-untabify] > + 'electric-pair-backward-delete-char-untabify) > + (define-key map [remap backward-delete-char] > + 'electric-pair-backward-delete-char) > + (define-key map [remap delete-backward-char] > + 'electric-pair-backward-delete-char) > + map) > + "Keymap used by `electric-pair-mode'.") > > ;;; Electric newlines after/before/around some chars. > > -(defvar electric-layout-rules '() > +(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', or a function of no > -arguments that returns one of those symbols.") > + > +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. > + > +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.") > > (defun electric-layout-post-self-insert-function () > (let* ((rule (cdr (assq last-command-event electric-layout-rules))) > @@ -469,23 +873,32 @@ arguments that returns one of those symbols.") > (setq pos (electric--after-char-pos)) > ;; Not in a string or comment. > (not (nth 8 (save-excursion (syntax-ppss pos))))) > - (let ((end (copy-marker (point) t))) > + (let ((end (copy-marker (point))) > + (sym (if (functionp rule) (funcall rule) rule))) > + (set-marker-insertion-type end (not (eq sym 'after-stay))) > (goto-char pos) > - (pcase (if (functionp rule) (funcall rule) rule) > + (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")) ; FIXME: check eolp before > inserting \n? > + (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 (1- pos)) (skip-chars-backward " \t") > + (unless (bolp) (insert "\n"))) > + (insert "\n"))) ; FIXME: check eolp before > inserting \n? > (goto-char end))))) > > +(put 'electric-layout-post-self-insert-function 'priority 40) > + > ;;;###autoload > (define-minor-mode electric-layout-mode > "Automatically insert newlines around some chars. > @@ -494,11 +907,13 @@ 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 > - (add-hook 'post-self-insert-hook > - #'electric-layout-post-self-insert-function) > - (remove-hook 'post-self-insert-hook > - #'electric-layout-post-self-insert-function))) > + (cond (electric-layout-mode > + (add-hook 'post-self-insert-hook > + #'electric-layout-post-self-insert-function) > + (electric--sort-post-self-insertion-hook)) > + (t > + (remove-hook 'post-self-insert-hook > + #'electric-layout-post-self-insert-function)))) > > (provide 'electric) > > diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el > index b7bd33f..f1eae18 100644 > --- a/lisp/emacs-lisp/lisp-mode.el > +++ b/lisp/emacs-lisp/lisp-mode.el > @@ -472,7 +472,13 @@ font-lock keywords will not be case sensitive." > (font-lock-mark-block-function . mark-defun) > (font-lock-syntactic-face-function > . lisp-font-lock-syntactic-face-function))) > - (setq-local prettify-symbols-alist lisp--prettify-symbols-alist)) > + (setq-local prettify-symbols-alist lisp--prettify-symbols-alist) > + ;; electric > + (when elisp > + (setq-local electric-pair-text-pairs > + (cons '(?\` . ?\') electric-pair-text-pairs))) > + (setq-local electric-pair-skip-whitespace 'chomp) > + (setq-local electric-pair-open-newline-between-pairs nil)) > > (defun lisp-outline-level () > "Lisp mode `outline-level' function." > diff --git a/lisp/simple.el b/lisp/simple.el > index a654351..624d87f 100644 > --- a/lisp/simple.el > +++ b/lisp/simple.el > @@ -610,7 +610,7 @@ In some text modes, where TAB inserts a tab, this > command indents to the > column specified by the function `current-left-margin'." > (interactive "*") > (delete-horizontal-space t) > - (newline) > + (newline 1 t) > (indent-according-to-mode)) > > (defun reindent-then-newline-and-indent () > @@ -6448,10 +6448,14 @@ More precisely, a char with closeparen syntax is > self-inserted.") > (point)))))) > (funcall blink-paren-function))) > > +(put 'blink-paren-post-self-insert-function 'priority 100) > + > (add-hook 'post-self-insert-hook #'blink-paren-post-self-insert-function > ;; Most likely, this hook is nil, so this arg doesn't matter, > ;; but I use it as a reminder that this function usually > - ;; likes to be run after others since it does `sit-for'. > + ;; likes to be run after others since it does > + ;; `sit-for'. That's also the reason it get a `priority' prop > + ;; of 100. > 'append) > > ;; This executes C-g typed while Emacs is waiting for a command. > diff --git a/test/automated/electric-tests.el > b/test/automated/electric-tests.el > new file mode 100644 > index 0000000..aa4a063 > --- /dev/null > +++ b/test/automated/electric-tests.el > @@ -0,0 +1,509 @@ > +;;; electric-tests.el --- tests for electric.el > + > +;; Copyright (C) 2013 Jo=C3=A3o T=C3=A1vora > + > +;; Author: Jo=C3=A3o T=C3=A1vora > +;; Keywords: > + > +;; This program is free software; you can redistribute it and/or modify > +;; it under the terms of the GNU General Public License as published by > +;; the Free Software Foundation, either version 3 of the License, or > +;; (at your option) any later version. > + > +;; This program is distributed in the hope that it will be useful, > +;; but WITHOUT ANY WARRANTY; without even the implied warranty of > +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +;; GNU General Public License for more details. > + > +;; You should have received a copy of the GNU General Public License > +;; along with this program. If not, see . > + > +;;; Commentary: > + > +;; > + > +;;; Code: > +(require 'ert) > +(require 'ert-x) > +(require 'electric) > +(require 'cl-lib) > + > +(defun call-with-saved-electric-modes (fn) > + (let ((saved-electric (if electric-pair-mode 1 -1)) > + (saved-layout (if electric-layout-mode 1 -1)) > + (saved-indent (if electric-indent-mode 1 -1))) > + (electric-pair-mode -1) > + (electric-layout-mode -1) > + (electric-indent-mode -1) > + (unwind-protect > + (funcall fn) > + (electric-pair-mode saved-electric) > + (electric-indent-mode saved-indent) > + (electric-layout-mode saved-layout)))) > + > +(defmacro save-electric-modes (&rest body) > + (declare (indent defun) (debug t)) > + `(call-with-saved-electric-modes #'(lambda () ,@body))) > + > +(defun electric-pair-test-for (fixture where char expected-string > + expected-point mode bindings > fixture-fn) > + (with-temp-buffer > + (funcall mode) > + (insert fixture) > + (save-electric-modes > + (let ((last-command-event char)) > + (goto-char where) > + (funcall fixture-fn) > + (cl-progv > + (mapcar #'car bindings) > + (mapcar #'cdr bindings) > + (self-insert-command 1)))) > + (should (equal (buffer-substring-no-properties (point-min) > (point-max)) > + expected-string)) > + (should (equal (point) > + expected-point)))) > + > +(eval-when-compile > + (defun electric-pair-define-test-form (name fixture > + char > + pos > + expected-string > + expected-point > + skip-pair-string > + prefix > + suffix > + extra-desc > + mode > + bindings > + fixture-fn) > + (let* ((expected-string-and-point > + (if skip-pair-string > + (with-temp-buffer > + (cl-progv > + ;; FIXME: avoid `eval' > + (mapcar #'car (eval bindings)) > + (mapcar #'cdr (eval bindings)) > + (funcall mode) > + (insert fixture) > + (goto-char (1+ pos)) > + (insert char) > + (cond ((eq (aref skip-pair-string pos) > + ?p) > + (insert (cadr (electric-pair-syntax-info > char))) > + (backward-char 1)) > + ((eq (aref skip-pair-string pos) > + ?s) > + (delete-char -1) > + (forward-char 1))) > + (list > + (buffer-substring-no-properties (point-min) > (point-max)) > + (point)))) > + (list expected-string expected-point))) > + (expected-string (car expected-string-and-point)) > + (expected-point (cadr expected-string-and-point)) > + (fixture (format "%s%s%s" prefix fixture suffix)) > + (expected-string (format "%s%s%s" prefix expected-string > suffix)) > + (expected-point (+ (length prefix) expected-point)) > + (pos (+ (length prefix) pos))) > + `(ert-deftest ,(intern (format > "electric-pair-%s-at-point-%s-in-%s%s" > + name > + (1+ pos) > + mode > + extra-desc)) > + () > + ,(format "With \"%s\", try input %c at point %d. \ > +Should %s \"%s\" and point at %d" > + fixture > + char > + (1+ pos) > + (if (string=3D fixture expected-string) > + "stay" > + "become") > + (replace-regexp-in-string "\n" "\\\\n" expected-string= ) > + expected-point) > + (electric-pair-test-for ,fixture > + ,(1+ pos) > + ,char > + ,expected-string > + ,expected-point > + ',mode > + ,bindings > + ,fixture-fn))))) > + > +(cl-defmacro define-electric-pair-test > + (name fixture > + input > + &key > + skip-pair-string > + expected-string > + expected-point > + bindings > + (modes '(quote (emacs-lisp-mode ruby-mode c++-mode))) > + (test-in-comments t) > + (test-in-strings t) > + (test-in-code t) > + (fixture-fn #'(lambda () > + (electric-pair-mode 1)))) > + `(progn > + ,@(cl-loop > + for mode in (eval modes) ;FIXME: avoid `eval' > + append > + (cl-loop > + for (prefix suffix extra-desc) in > + (append (if test-in-comments > + `((,(with-temp-buffer > + (funcall mode) > + (insert "z") > + (comment-region (point-min) (point-max)) > + (buffer-substring-no-properties (point-min) > + (1- > (point-max)))) > + "" > + "-in-comments"))) > + (if test-in-strings > + `(("\"" "\"" "-in-strings"))) > + (if test-in-code > + `(("" "" "")))) > + append > + (cl-loop > + for char across input > + for pos from 0 > + unless (eq char ?-) > + collect (electric-pair-define-test-form > + name > + fixture > + (aref input pos) > + pos > + expected-string > + expected-point > + skip-pair-string > + prefix > + suffix > + extra-desc > + mode > + bindings > + fixture-fn)))))) > + > +;;; Basic pairings and skippings > +;;; > +(define-electric-pair-test balanced-situation > + " (()) " "(((((((" :skip-pair-string "ppppppp" > + :modes '(ruby-mode)) > + > +(define-electric-pair-test too-many-openings > + " ((()) " "(((((((" :skip-pair-string "ppppppp") > + > +(define-electric-pair-test too-many-closings > + " (())) " "(((((((" :skip-pair-string "------p") > + > +(define-electric-pair-test too-many-closings-2 > + "() ) " "---(---" :skip-pair-string "-------") > + > +(define-electric-pair-test too-many-closings-3 > + ")() " "(------" :skip-pair-string "-------") > + > +(define-electric-pair-test balanced-autoskipping > + " (()) " "---))--" :skip-pair-string "---ss--") > + > +(define-electric-pair-test too-many-openings-autoskipping > + " ((()) " "----))-" :skip-pair-string "-------") > + > +(define-electric-pair-test too-many-closings-autoskipping > + " (())) " "---)))-" :skip-pair-string "---sss-") > + > + > +;;; Mixed parens > +;;; > +(define-electric-pair-test mixed-paren-1 > + " ()] " "-(-(---" :skip-pair-string "-p-p---") > + > +(define-electric-pair-test mixed-paren-2 > + " [() " "-(-()--" :skip-pair-string "-p-ps--") > + > +(define-electric-pair-test mixed-paren-3 > + " (]) " "-(-()--" :skip-pair-string "---ps--") > + > +(define-electric-pair-test mixed-paren-4 > + " ()] " "---)]--" :skip-pair-string "---ss--") > + > +(define-electric-pair-test mixed-paren-5 > + " [() " "----(--" :skip-pair-string "----p--") > + > +(define-electric-pair-test find-matching-different-paren-type > + " ()] " "-[-----" :skip-pair-string "-------") > + > +(define-electric-pair-test find-matching-different-paren-type-inside-lis= t > + "( ()]) " "-[-----" :skip-pair-string "-------") > + > +(define-electric-pair-test ignore-different-unmatching-paren-type > + "( ()]) " "-(-----" :skip-pair-string "-p-----") > + > +(define-electric-pair-test autopair-keep-least-amount-of-mixed-unbalance > + "( ()] " "-(-----" :skip-pair-string "-p-----") > + > +(define-electric-pair-test dont-autopair-to-resolve-mixed-unbalance > + "( ()] " "-[-----" :skip-pair-string "-------") > + > +(define-electric-pair-test > autopair-so-as-not-to-worsen-unbalance-situation > + "( (]) " "-[-----" :skip-pair-string "-p-----") > + > +(define-electric-pair-test skip-over-partially-balanced > + " [([]) " "-----)---" :skip-pair-string "-----s---") > + > +(define-electric-pair-test > only-skip-over-at-least-partially-balanced-stuff > + " [([()) " "-----))--" :skip-pair-string "-----s---") > + > + > + > + > +;;; Quotes > +;;; > +(define-electric-pair-test pair-some-quotes-skip-others > + " \"\" " "-\"\"-----" :skip-pair-string "-ps------" > + :test-in-strings nil > + :bindings `((electric-pair-text-syntax-table > + . ,prog-mode-syntax-table))) > + > +(define-electric-pair-test skip-single-quotes-in-ruby-mode > + " '' " "--'-" :skip-pair-string "--s-" > + :modes '(ruby-mode) > + :test-in-comments nil > + :test-in-strings nil > + :bindings `((electric-pair-text-syntax-table > + . ,prog-mode-syntax-table))) > + > +(define-electric-pair-test leave-unbalanced-quotes-alone > + " \"' " "-\"'-" :skip-pair-string "----" > + :modes '(ruby-mode) > + :test-in-strings nil > + :bindings `((electric-pair-text-syntax-table > + . ,prog-mode-syntax-table))) > + > +(define-electric-pair-test leave-unbalanced-quotes-alone-2 > + " \"\\\"' " "-\"--'-" :skip-pair-string "------" > + :modes '(ruby-mode) > + :test-in-strings nil > + :bindings `((electric-pair-text-syntax-table > + . ,prog-mode-syntax-table))) > + > +(define-electric-pair-test leave-unbalanced-quotes-alone-3 > + " foo\\''" "'------" :skip-pair-string "-------" > + :modes '(ruby-mode) > + :test-in-strings nil > + :bindings `((electric-pair-text-syntax-table > + . ,prog-mode-syntax-table))) > + > +(define-electric-pair-test inhibit-only-if-next-is-mismatched > + "\"foo\"\"bar" "\"" > + :expected-string "\"\"\"foo\"\"bar" > + :expected-point 2 > + :test-in-strings nil > + :bindings `((electric-pair-text-syntax-table > + . ,prog-mode-syntax-table))) > + > + > +;;; More quotes, but now don't bind `electric-pair-text-syntax-table' > +;;; to `prog-mode-syntax-table'. Use the defaults for > +;;; `electric-pair-pairs' and `electric-pair-text-pairs'. > +;;; > +(define-electric-pair-test pairing-skipping-quotes-in-code > + " \"\" " "-\"\"-----" :skip-pair-string "-ps------" > + :test-in-strings nil > + :test-in-comments nil) > + > +(define-electric-pair-test skipping-quotes-in-comments > + " \"\" " "--\"-----" :skip-pair-string "--s------" > + :test-in-strings nil) > + > + > +;;; Skipping over whitespace > +;;; > +(define-electric-pair-test whitespace-jumping > + " ( ) " "--))))---" :expected-string " ( ) " :expected-point 8 > + :bindings '((electric-pair-skip-whitespace . t))) > + > +(define-electric-pair-test whitespace-chomping > + " ( ) " "--)------" :expected-string " () " :expected-point 4 > + :bindings '((electric-pair-skip-whitespace . chomp))) > + > +(define-electric-pair-test whitespace-chomping-2 > + " ( \n\t\t\n ) " "--)------" :expected-string " () " :expected-poin= t > 4 > + :bindings '((electric-pair-skip-whitespace . chomp)) > + :test-in-comments nil) > + > +(define-electric-pair-test whitespace-chomping-dont-cross-comments > + " ( \n\t\t\n ) " "--)------" :expected-string " () \n\t\t\n ) " > + :expected-point 4 > + :bindings '((electric-pair-skip-whitespace . chomp)) > + :test-in-strings nil > + :test-in-code nil > + :test-in-comments t) > + > + > +;;; Pairing arbitrary characters > +;;; > +(define-electric-pair-test angle-brackets-everywhere > + "<>" "<>" :skip-pair-string "ps" > + :bindings '((electric-pair-pairs . ((?\< . ?\>))))) > + > +(define-electric-pair-test angle-brackets-everywhere-2 > + "(<>" "-<>" :skip-pair-string "-ps" > + :bindings '((electric-pair-pairs . ((?\< . ?\>))))) > + > +(defvar electric-pair-test-angle-brackets-table > + (let ((table (make-syntax-table prog-mode-syntax-table))) > + (modify-syntax-entry ?\< "(>" table) > + (modify-syntax-entry ?\> ")<`" table) > + table)) > + > +(define-electric-pair-test angle-brackets-pair > + "<>" "<" :expected-string "<><>" :expected-point 2 > + :test-in-code nil > + :bindings `((electric-pair-text-syntax-table > + . ,electric-pair-test-angle-brackets-table))) > + > +(define-electric-pair-test angle-brackets-skip > + "<>" "->" :expected-string "<>" :expected-point 3 > + :test-in-code nil > + :bindings `((electric-pair-text-syntax-table > + . ,electric-pair-test-angle-brackets-table))) > + > +(define-electric-pair-test pair-backtick-and-quote-in-comments > + ";; " "---`" :expected-string ";; `'" :expected-point 5 > + :test-in-comments nil > + :test-in-strings nil > + :modes '(emacs-lisp-mode) > + :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) > + > +(define-electric-pair-test skip-backtick-and-quote-in-comments > + ";; `foo'" "-------'" :expected-string ";; `foo'" :expected-point 9 > + :test-in-comments nil > + :test-in-strings nil > + :modes '(emacs-lisp-mode) > + :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) > + > +(define-electric-pair-test pair-backtick-and-quote-in-strings > + "\"\"" "-`" :expected-string "\"`'\"" :expected-point 3 > + :test-in-comments nil > + :test-in-strings nil > + :modes '(emacs-lisp-mode) > + :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) > + > +(define-electric-pair-test skip-backtick-and-quote-in-strings > + "\"`'\"" "--'" :expected-string "\"`'\"" :expected-point 4 > + :test-in-comments nil > + :test-in-strings nil > + :modes '(emacs-lisp-mode) > + :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) > + > +(define-electric-pair-test skip-backtick-and-quote-in-strings-2 > + " \"`'\"" "----'" :expected-string " \"`'\"" :expected-point 6 > + :test-in-comments nil > + :test-in-strings nil > + :modes '(emacs-lisp-mode) > + :bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) > + > + > +;;; `js-mode' has `electric-layout-rules' for '{ and '} > +;;; > +(define-electric-pair-test js-mode-braces > + "" "{" :expected-string "{}" :expected-point 2 > + :modes '(js-mode) > + :fixture-fn #'(lambda () > + (electric-pair-mode 1))) > + > +(define-electric-pair-test js-mode-braces-with-layout > + "" "{" :expected-string "{\n\n}" :expected-point 3 > + :modes '(js-mode) > + :test-in-comments nil > + :test-in-strings nil > + :fixture-fn #'(lambda () > + (electric-layout-mode 1) > + (electric-pair-mode 1))) > + > +(define-electric-pair-test js-mode-braces-with-layout-and-indent > + "" "{" :expected-string "{\n \n}" :expected-point 7 > + :modes '(js-mode) > + :test-in-comments nil > + :test-in-strings nil > + :fixture-fn #'(lambda () > + (electric-pair-mode 1) > + (electric-indent-mode 1) > + (electric-layout-mode 1))) > + > + > +;;; Backspacing > +;;; TODO: better tests > +;;; > +(ert-deftest electric-pair-backspace-1 () > + (save-electric-modes > + (with-temp-buffer > + (insert "()") > + (goto-char 2) > + (electric-pair-backward-delete-char 1) > + (should (equal "" (buffer-string)))))) > + > + > +;;; Electric newlines between pairs > +;;; TODO: better tests > +(ert-deftest electric-pair-open-extra-newline () > + (save-electric-modes > + (with-temp-buffer > + (c-mode) > + (electric-pair-mode 1) > + (electric-indent-mode 1) > + (insert "int main {}") > + (backward-char 1) > + (let ((c-basic-offset 4)) > + (newline 1 t) > + (should (equal "int main {\n \n}" > + (buffer-string))) > + (should (equal (point) (- (point-max) 2))))))) > + > + > + > +;;; Autowrapping > +;;; > +(define-electric-pair-test autowrapping-1 > + "foo" "(" :expected-string "(foo)" :expected-point 2 > + :fixture-fn #'(lambda () > + (electric-pair-mode 1) > + (mark-sexp 1))) > + > +(define-electric-pair-test autowrapping-2 > + "foo" ")" :expected-string "(foo)" :expected-point 6 > + :fixture-fn #'(lambda () > + (electric-pair-mode 1) > + (mark-sexp 1))) > + > +(define-electric-pair-test autowrapping-3 > + "foo" ")" :expected-string "(foo)" :expected-point 6 > + :fixture-fn #'(lambda () > + (electric-pair-mode 1) > + (goto-char (point-max)) > + (skip-chars-backward "\"") > + (mark-sexp -1))) > + > +(define-electric-pair-test autowrapping-4 > + "foo" "(" :expected-string "(foo)" :expected-point 2 > + :fixture-fn #'(lambda () > + (electric-pair-mode 1) > + (goto-char (point-max)) > + (skip-chars-backward "\"") > + (mark-sexp -1))) > + > +(define-electric-pair-test autowrapping-5 > + "foo" "\"" :expected-string "\"foo\"" :expected-point 2 > + :fixture-fn #'(lambda () > + (electric-pair-mode 1) > + (mark-sexp 1))) > + > +(define-electric-pair-test autowrapping-6 > + "foo" "\"" :expected-string "\"foo\"" :expected-point 6 > + :fixture-fn #'(lambda () > + (electric-pair-mode 1) > + (goto-char (point-max)) > + (skip-chars-backward "\"") > + (mark-sexp -1))) > + > +(provide 'electric-pair-tests) > +;;; electric-pair-tests.el ends here > > --089e015383e41aee8b04ee489231 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
Not sure if Stefan saw your message here, but he mentioned= in the "feature freeze" thread that your patch is approved and c= an be installed. Have a look at it.

On 23 December 2013 16:41, Jo=C3=A3o T=C3=A1vora= <joaotavora@gmail.com> wrote:

Stefan,

Don't want to be pushy, but is there anything holding up this patch?
Here's yet another version that fixes some more details:

- fixed default values for `electric-pair-inhibit-predicate' and
=C2=A0 `electric-pair-skip-self'.

- electric-pair-mode's docstring doesn't mention related customizat= ion
=C2=A0 variables because there are now too many of them.

- emacs-lisp/lisp-mode.el locally sets
=C2=A0 `electric-pair-open-newline-between-pairs' to t.

- the newline call in `newline-and-indent` now uses interactive set to
=C2=A0 t

Thanks
Jo=C3=A3o

diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 12889de..0564484 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,25 @@
+2013-12-99 =C2=A0Jo=C3=83=C2=A3o T=C3=83=C2=A1vora <joaotavora@gmail.com>
+
+ =C2=A0 =C2=A0 =C2=A0 * electric.el (electric-pair-mode): More flexible en= gine for skip-
+ =C2=A0 =C2=A0 =C2=A0 and inhibit predicates, new options for pairing-rela= ted
+ =C2=A0 =C2=A0 =C2=A0 functionality.
+ =C2=A0 =C2=A0 =C2=A0 (electric-pair-preserve-balance): Pair/skip parenthe= ses and quotes
+ =C2=A0 =C2=A0 =C2=A0 if that keeps or improves their balance in buffers.<= br> + =C2=A0 =C2=A0 =C2=A0 (electric-pair-delete-adjacent-pairs): Delete the pa= ir when
+ =C2=A0 =C2=A0 =C2=A0 backspacing over adjacent matched delimiters.
+ =C2=A0 =C2=A0 =C2=A0 (electric-pair-open-extra-newline): Open extra newli= ne when
+ =C2=A0 =C2=A0 =C2=A0 inserting newlines between adjacent matched delimite= rs.
+ =C2=A0 =C2=A0 =C2=A0 (electric--sort-post-self-insertion-hook): Sort
+ =C2=A0 =C2=A0 =C2=A0 post-self-insert-hook according to priority values w= hen
+ =C2=A0 =C2=A0 =C2=A0 minor-modes are activated.
+ =C2=A0 =C2=A0 =C2=A0 * simple.el (newline-and-indent): Call newline with = interactive
+ =C2=A0 =C2=A0 =C2=A0 set to t.
+ =C2=A0 =C2=A0 =C2=A0 (blink-paren-post-self-insert-function): Set priorit= y to 100.
+ =C2=A0 =C2=A0 =C2=A0 * emacs-lisp/lisp-mode.el (lisp-mode-variables): Use=
+ =C2=A0 =C2=A0 =C2=A0 electric-pair-text-pairs to pair backtick-and-quote = in strings and
+ =C2=A0 =C2=A0 =C2=A0 comments. Locally set electric-pair-skip-white= space to 'chomp and
+ =C2=A0 =C2=A0 =C2=A0 electric-pair-open-newline-between-pairs to nil.
+
=C2=A02013-12-23 =C2=A0Chong Yidong =C2=A0<cyd@gnu.org>

=C2=A0 =C2=A0 =C2=A0 =C2=A0 * subr.el (set-transient-map): Rename from
diff --git a/lisp/electric.el b/lisp/electric.el
index 91b99b4..ca4616b 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -187,6 +187,17 @@ Returns nil when we can't find this char." =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 (eq (char-before) last-command-event)))))
=C2=A0 =C2=A0 =C2=A0 =C2=A0pos)))

+(defun electric--sort-post-self-insertion-hook ()
+ =C2=A0"Ensure order of electric functions in `post-self-insertion-ho= ok'.
+
+Hooks in this variable interact in non-trivial ways, so a
+relative order must be maintained within it."
+ =C2=A0(setq-default post-self-insert-hook
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(sort (default-val= ue 'post-self-insert-hook)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0#'(lambda (fn1 fn2)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0(< (or (get fn1 'priority) 0)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 (or (get fn2 'priority) 0))))))
+
=C2=A0;;; Electric indentation.

=C2=A0;; Autoloading variables is generally undesirable, but major modes @@ -267,6 +278,8 @@ mode set `electric-indent-inhibit', but this can be= used as a workaround.")
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (>= pos (line-beginning-position)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(indent-according-to-mode)))))

+(put 'electric-indent-post-self-insert-function 'priority =C2=A060= )
+
=C2=A0(defun electric-indent-just-newline (arg)
=C2=A0 =C2=A0"Insert just a newline, without any auto-indentation.&quo= t;
=C2=A0 =C2=A0(interactive "*P")
@@ -295,20 +308,9 @@ insert a character from `electric-indent-chars'.&q= uot;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 #'electric-indent-post-self-insert-function))
=C2=A0 =C2=A0 =C2=A0(when (eq (lookup-key global-map [?\C-j]) 'newline-= and-indent)
=C2=A0 =C2=A0 =C2=A0 =C2=A0(define-key global-map [?\C-j] 'electric-ind= ent-just-newline))
- =C2=A0 =C2=A0;; post-self-insert-hooks interact in non-trivial ways.
- =C2=A0 =C2=A0;; It turns out that electric-indent-mode generally works be= tter if run
- =C2=A0 =C2=A0;; late, but still before blink-paren.
=C2=A0 =C2=A0 =C2=A0(add-hook 'post-self-insert-hook
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0#'electric-indent-pos= t-self-insert-function
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'append)
- =C2=A0 =C2=A0;; FIXME: Ugly!
- =C2=A0 =C2=A0(let ((bp (memq #'blink-paren-post-self-insert-function<= br> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(def= ault-value 'post-self-insert-hook))))
- =C2=A0 =C2=A0 =C2=A0(when (memq #'electric-indent-post-self-insert-fu= nction bp)
- =C2=A0 =C2=A0 =C2=A0 =C2=A0(setcar bp #'electric-indent-post-self-ins= ert-function)
- =C2=A0 =C2=A0 =C2=A0 =C2=A0(setcdr bp (cons #'blink-paren-post-self-i= nsert-function
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (delq #'electric-indent-post-self-insert-function
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cdr bp))))))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0#'electric-indent-pos= t-self-insert-function)
+ =C2=A0 =C2=A0(electric--sort-post-self-insertion-hook)))

=C2=A0;;;###autoload
=C2=A0(define-minor-mode electric-indent-local-mode
@@ -327,32 +329,163 @@ insert a character from `electric-indent-chars'.= "

=C2=A0(defcustom electric-pair-pairs
=C2=A0 =C2=A0'((?\" . ?\"))
- =C2=A0"Alist of pairs that should be used regardless of major mode.&= quot;
+ =C2=A0"Alist of pairs that should be used regardless of major mode.<= br> +
+Pairs of delimiters in this list are a fallback in case they have
+no syntax relevant to `electric-pair-mode' in the mode's syntax +table.
+
+See also the variable `electric-pair-text-pairs'."
=C2=A0 =C2=A0:version "24.1"
=C2=A0 =C2=A0:type '(repeat (cons character character)))

-(defcustom electric-pair-skip-self t
+(defcustom electric-pair-text-pairs
+ =C2=A0'((?\" . ?\" ))
+ =C2=A0"Alist of pairs that should always be used in comments and str= ings.
+
+Pairs of delimiters in this list are a fallback in case they have
+no syntax relevant to `electric-pair-mode' in the syntax table
+defined in `electric-pair-text-syntax-table'"
+ =C2=A0:version "24.4"
+ =C2=A0:type '(repeat (cons character character)))
+
+(defcustom electric-pair-skip-self #'electric-pair-default= -skip-self
=C2=A0 =C2=A0"If non-nil, skip char instead of inser= ting a second closing paren.
+
=C2=A0When inserting a closing paren character right before the same charac= ter,
=C2=A0just skip that character instead, so that hitting ( followed by ) res= ults
=C2=A0in \"()\" rather than \"())\".
-This can be convenient for people who find it easier to hit ) than C-f.&qu= ot;
+
+This can be convenient for people who find it easier to hit ) than C-f. +
+Can also be a function of one argument (the closer char just
+inserted), in which case that function's return value is
+considered instead."
=C2=A0 =C2=A0:version "24.1"
- =C2=A0:type 'boolean)
+ =C2=A0:type '(choice
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Never skip" nil)=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Help balance" el= ectric-pair-default-skip-self)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Always skip" t)<= br> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0function))

=C2=A0(defcustom electric-pair-inhibit-predicate
=C2=A0 =C2=A0#'electric-pair-default-inhibit
=C2=A0 =C2=A0"Predicate to prevent insertion of= a matching pair.
+
=C2=A0The function is called with a single char (the opening char just inse= rted).
=C2=A0If it returns non-nil, then `electric-pair-mode' will not insert = a matching
=C2=A0closer."
=C2=A0 =C2=A0:version "24.4"
=C2=A0 =C2=A0:type '(choice
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Default" electri= c-pair-default-inhibit)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Conservative" el= ectric-pair-conservative-inhibit)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Help balance" el= ectric-pair-default-inhibit)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Always pair"= ; ignore)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0function))

-(defun electric-pair-default-inhibit (char)
+(defcustom electric-pair-preserve-balance t
+ =C2=A0"Non-nil if default pairing and skipping should help balance p= arentheses.
+
+The default values of `electric-pair-inhibit-predicate' and
+`electric-pair-skip-self' check this variable before delegating to oth= er
+predicates reponsible for making decisions on whether to pair/skip some +characters based on the actual state of the buffer's parenthesis and +quotes."
+ =C2=A0:version "24.4"
+ =C2=A0:type 'boolean)
+
+(defcustom electric-pair-delete-adjacent-pairs t
+ =C2=A0"If non-nil, backspacing an open paren also deletes adjacent c= loser.
+
+Can also be a function of no arguments, in which case that function's<= br> +return value is considered instead."
+ =C2=A0:version "24.4"
+ =C2=A0:type '(choice
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Yes" t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "No" nil)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0function))
+
+(defcustom electric-pair-open-newline-between-pairs t
+ =C2=A0"If non-nil, a newline between adjacent parentheses opens an e= xtra one.
+
+Can also be a function of no arguments, in which case that function's<= br> +return value is considered instead."
+ =C2=A0:version "24.4"
+ =C2=A0:type '(choice
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Yes" t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "No" nil)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0function))
+
+(defcustom electric-pair-skip-whitespace t
+ =C2=A0"If non-nil skip whitespace when skipping over closing parens.=
+
+The specific kind of whitespace skipped is given by the variable
+`electric-pair-skip-whitespace-chars'.
+
+The symbol `chomp' specifies that the skipped-over whitespace
+should be deleted.
+
+Can also be a function of no arguments, in which case that function's<= br> +return value is considered instead."
+ =C2=A0:version "24.4"
+ =C2=A0:type '(choice
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Yes, jump over whites= pace" t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "Yes, and delete white= space" 'chomp)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(const :tag "No, no whitespace ski= pping" nil)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0function))
+
+(defcustom electric-pair-skip-whitespace-chars (list ?\t ?\s ?\n)
+ =C2=A0"Whitespace characters considered by `electric-pair-skip-white= space'."
+ =C2=A0:version "24.4"
+ =C2=A0:type '(choice (set (const :tag "Space" ?\s)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(const :tag "Tab" ?\t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(const :tag "Newline" ?\n))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (list character))= )
+
+(defun electric-pair--skip-whitespace ()
+ =C2=A0"Skip whitespace forward, not crossing comment or string bound= aries."
+ =C2=A0(let ((saved (point))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(string-or-comment (nth 8 (syntax-ppss))))
+ =C2=A0 =C2=A0(skip-chars-forward (apply #'string electric-pair-skip-w= hitespace-chars))
+ =C2=A0 =C2=A0(unless (eq string-or-comment (nth 8 (syntax-ppss)))
+ =C2=A0 =C2=A0 =C2=A0(goto-char saved))))
+
+(defvar electric-pair-text-syntax-table prog-mode-syntax-table
+ =C2=A0"Syntax table used when pairing inside comments and strings. +
+`electric-pair-mode' considers this syntax table only when point in in= side
+quotes or comments. If lookup fails here, `electric-pair-text-pairs' w= ill
+be considered.")
+
+(defun electric-pair-backward-delete-char (n &optional killflag untabi= fy)
+ =C2=A0"Delete characters backward, and maybe also two adjacent paire= d delimiters.
+
+Remaining behaviour is given by `backward-delete-char' or, if UNTABIFY= is
+non-nil, `backward-delete-char-untabify'."
+ =C2=A0(interactive "*p\nP")
+ =C2=A0(let* ((prev (char-before))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (next (char-after))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (syntax-info (electric-pair-syntax-info prev)= )
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (syntax (car syntax-info))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (pair (cadr syntax-info)))
+ =C2=A0 =C2=A0(when (and (if (functionp electric-pair-delete-adjacent-pair= s)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (funcall e= lectric-pair-delete-adjacent-pairs)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 electric-pair-del= ete-adjacent-pairs)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 next
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (memq syntax '(?\( ?= \" ?\$))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (eq pair next))
+ =C2=A0 =C2=A0 =C2=A0(delete-char 1 killflag))
+ =C2=A0 =C2=A0(if untabify
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(backward-delete-char-untabify n killflag)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(backward-delete-char n killflag))))
+
+(defun electric-pair-backward-delete-char-untabify (n &optional killfl= ag)
+ =C2=A0"Delete characters backward, and maybe also two adjacent paire= d delimiters.
+
+Remaining behaviour is given by `backward-delete-char-untabify'."=
+ =C2=A0(interactive "*p\nP")
+ =C2=A0(electric-pair-backward-delete-char n killflag t))
+
+(defun electric-pair-conservative-inhibit (char)
=C2=A0 =C2=A0(or
=C2=A0 =C2=A0 ;; I find it more often preferable not to pair when the
=C2=A0 =C2=A0 ;; same char is next.
@@ -363,14 +496,40 @@ closer."
=C2=A0 =C2=A0 ;; I also find it often preferable not to pair next to a word= .
=C2=A0 =C2=A0 (eq (char-syntax (following-char)) ?w)))

-(defun electric-pair-syntax (command-event)
- =C2=A0(let ((x (assq command-event electric-pair-pairs)))
+(defun electric-pair-syntax-info (command-event)
+ =C2=A0"Calculate a list (SYNTAX PAIR UNCONDITIONAL STRING-OR-COMMENT= -START).
+
+SYNTAX is COMMAND-EVENT's syntax character. =C2=A0PAIR is
+COMMAND-EVENT's pair. =C2=A0UNCONDITIONAL indicates the variables
+`electric-pair-pairs' or `electric-pair-text-pairs' were used to +lookup syntax. =C2=A0STRING-OR-COMMENT-START indicates that point is
+inside a comment of string."
+ =C2=A0(let* ((pre-string-or-comment (nth 8 (save-excursion
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (syntax-= ppss (1- (point))))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (post-string-or-comment (nth 8 (syntax-ppss (= point))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (string-or-comment (and post-string-or-commen= t
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pre-string-or-comment))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (table (if string-or-comment
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0elec= tric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(syntax-tab= le)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (table-syntax-and-pair (with-syntax-table tab= le
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(list (char-syntax command-eve= nt)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(or (matc= hing-paren command-event)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0command-event))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (fallback (if string-or-comment
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 (append electric-pair-text-pairs
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 electric-pair-pairs)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ele= ctric-pair-pairs))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (direct (assq command-event fallback))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (reverse (rassq command-event fallback)))
=C2=A0 =C2=A0 =C2=A0(cond
- =C2=A0 =C2=A0 (x (if (eq (car x) (cdr x)) ?\" ?\())
- =C2=A0 =C2=A0 ((rassq command-event electric-pair-pairs) ?\))
- =C2=A0 =C2=A0 ((nth 8 (syntax-ppss))
- =C2=A0 =C2=A0 =C2=A0(with-syntax-table text-mode-syntax-table (char-synta= x command-event)))
- =C2=A0 =C2=A0 (t (char-syntax command-event)))))
+ =C2=A0 =C2=A0 ((memq (car table-syntax-and-pair)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'(?\" ?\( ?\) ?\$))
+ =C2=A0 =C2=A0 =C2=A0(append table-syntax-and-pair (list nil string-or-com= ment)))
+ =C2=A0 =C2=A0 (direct (if (eq (car direct) (cdr direct))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (list ?\" co= mmand-event t string-or-comment)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (list ?\( (cdr direct) t= string-or-comment)))
+ =C2=A0 =C2=A0 (reverse (list ?\) (car reverse) t string-or-comment)))))
=C2=A0(defun electric-pair--insert (char)
=C2=A0 =C2=A0(let ((last-command-event char)
@@ -378,56 +537,286 @@ closer."
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (electric-pair-mode nil))
=C2=A0 =C2=A0 =C2=A0(self-insert-command 1)))

+(defun electric-pair--syntax-ppss (&optional pos where)
+ =C2=A0"Like `syntax-ppss', but sometimes fallback to `parse-part= ial-sexp'.
+
+WHERE is list defaulting to '(string comment) and indicates
+when to fallback to `parse-partial-sexp'."
+ =C2=A0(let* ((pos (or pos (point)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (where (or where '(string comment)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (quick-ppss (syntax-ppss))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (quick-ppss-at-pos (syntax-ppss pos)))
+ =C2=A0 =C2=A0(if (or (and (nth 3 quick-ppss) (memq 'string where)) + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(and (nth 4 quick-ppss) (memq &#= 39;comment where)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(with-syntax-table electric-pair-text-syntax-t= able
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(parse-partial-sexp (1+ (nth 8 quick-pp= ss)) pos))
+ =C2=A0 =C2=A0 =C2=A0;; HACK! cc-mode apparently has some `syntax-ppss'= ; bugs
+ =C2=A0 =C2=A0 =C2=A0(if (memq major-mode '(c-mode c++ mode))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(parse-partial-sexp (point-min) pos) + =C2=A0 =C2=A0 =C2=A0 =C2=A0quick-ppss-at-pos))))
+
+;; Balancing means controlling pairing and skipping of parentheses so
+;; that, if possible, the buffer ends up at least as balanced as
+;; before, if not more. The algorithm is slightly complex because some
+;; situations like "()))" need pairing to occur at the end but n= ot at
+;; the beginning. Balancing should also happen independently for
+;; different types of parentheses, so that having your {}'s unbalanced=
+;; doesn't keep `electric-pair-mode' from balancing your ()'s = and your
+;; []'s.
+(defun electric-pair--balance-info (direction string-or-comment)
+ =C2=A0"Examine lists forward or backward according to DIRECTIONS'= ;s sign.
+
+STRING-OR-COMMENT is info suitable for running `parse-partial-sexp'. +
+Return a cons of two descritions (MATCHED-P . PAIR) for the
+innermost and outermost lists that enclose point. The outermost
+list enclosing point is either the first top-level or first
+mismatched list found by uplisting.
+
+If the outermost list is matched, don't rely on its PAIR. If
+point is not enclosed by any lists, return ((T) (T))."
+ =C2=A0(let* (innermost
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 outermost
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (table (if string-or-comment
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0elec= tric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(syntax-tab= le)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (at-top-level-or-equivalent-fn
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; called when `scan-sexps' ran per= fectly, when when it
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; found a parenthesis pointing in the = direction of
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; travel. Also when travel started ins= ide a comment and
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; exited it
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0#'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(setq outermost (list t))=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(unless innermost
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(setq innermost (l= ist t)))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (ended-prematurely-fn
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; called when `scan-sexps' crashed= against a parenthesis
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; pointing opposite the direction of t= ravel. After
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; traversing that character, the idea = is to travel one sexp
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; in the opposite direction looking fo= r a matching
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; delimiter.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0#'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(let* ((pos (point))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (ma= tched
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(save-excursion
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(cond ((< direction 0)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (condition-case nil
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (eq (char-after pos)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (with-syntax-ta= ble table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (matchin= g-paren
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(c= har-before
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (= scan-sexps (point) 1)))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (scan-error nil)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(t
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; In this case, no need to use
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; `scan-sexps', we can use some
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; `electric-pair--syntax-ppss' in this=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; case (which uses the quicker
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; `syntax-ppss' in some cases)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (let* ((ppss (electric-pair--syntax-ppss + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 (1- (point))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(start (car (las= t (nth 9 ppss))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(opener (char-af= ter start)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (and start
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(eq (char-before= pos)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(o= r (with-syntax-table table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0(matching-paren opener))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0opener))))))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (ac= tual-pair (if (> direction 0)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(char-before (po= int))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(char-after (point)))))=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(unless innermost<= br> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(setq inner= most (cons matched actual-pair)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(unless matched + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(setq outer= most (cons matched actual-pair)))))))
+ =C2=A0 =C2=A0(save-excursion
+ =C2=A0 =C2=A0 =C2=A0(while (not outermost)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(condition-case err
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(with-syntax-table table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(scan-sexps (point) (if (= > direction 0)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(point-max)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(- (point-max))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(funcall at-top-level-or-= equivalent-fn))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(scan-error
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cond ((or
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; some er= ror happened and it is not of the "ended
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; prematu= rely" kind"...
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (not (stri= ng-match "ends prematurely" (nth 1 err)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; ... or = we were in a comment and just came out of
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; it.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (and strin= g-or-comment
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(not (nth 8 (syntax-ppss)))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(funcall at= -top-level-or-equivalent-fn))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (t
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; exit the= sexp
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char = (nth 3 err))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(funcall en= ded-prematurely-fn)))))))
+ =C2=A0 =C2=A0(cons innermost outermost)))
+
+(defun electric-pair--looking-at-unterminated-string-p (char)
+ =C2=A0"Say if following string starts with CHAR and is unterminated.= "
+ =C2=A0;; FIXME: ugly/naive
+ =C2=A0(save-excursion
+ =C2=A0 =C2=A0(skip-chars-forward (format "^%c" char))
+ =C2=A0 =C2=A0(while (not (zerop (% (save-excursion (skip-syntax-backward = "\\")) 2)))
+ =C2=A0 =C2=A0 =C2=A0(unless (eobp)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(forward-char 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(skip-chars-forward (format "^%c" ch= ar))))
+ =C2=A0 =C2=A0(and (not (eobp))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (condition-case err
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (progn (forward-sexp) nil)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (scan-error t)))))
+
+(defun electric-pair--inside-string-p (char)
+ =C2=A0"Say if point is inside a string started by CHAR.
+
+A comments text is parsed with `electric-pair-text-syntax-table'.
+Also consider strings within comments, but not strings within
+strings."
+ =C2=A0;; FIXME: could also consider strings within strings by examining + =C2=A0;; delimiters.
+ =C2=A0(let* ((ppss (electric-pair--syntax-ppss (point) '(comment))))<= br> + =C2=A0 =C2=A0(memq (nth 3 ppss) (list t char))))
+
+(defun electric-pair-inhibit-if-helps-balance (char)
+ =C2=A0"Return non-nil if auto-pairing of CHAR would hurt parentheses= ' balance.
+
+Works by first removing the character from the buffer, then doing
+some list calculations, finally restoring the situation as if nothing
+happened."
+ =C2=A0(pcase (electric-pair-syntax-info char)
+ =C2=A0 =C2=A0(`(,syntax ,pair ,_ ,s-or-c)
+ =C2=A0 =C2=A0 (unwind-protect
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (progn
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (delete-char -1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cond ((eq ?\( syntax)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(let* ((pai= r-data
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0(electric-pair--balance-info 1 s-or-c))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (innermost (car pair-data))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (outermost (cdr pair-data)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(con= d ((car outermost)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 nil)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0(t
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (eq (cdr outermost) pair)))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ((eq syntax ?\&qu= ot;)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air--looking-at-unterminated-string-p char))))
+ =C2=A0 =C2=A0 =C2=A0 (insert-char char)))))
+
+(defun electric-pair-skip-if-helps-balance (char)
+ =C2=A0"Return non-nil if skipping CHAR would benefit parentheses'= ; balance.
+
+Works by first removing the character from the buffer, then doing
+some list calculations, finally restoring the situation as if nothing
+happened."
+ =C2=A0(pcase (electric-pair-syntax-info char)
+ =C2=A0 =C2=A0(`(,syntax ,pair ,_ ,s-or-c)
+ =C2=A0 =C2=A0 (unwind-protect
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (progn
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (delete-char -1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cond ((eq syntax ?\))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(let* ((pai= r-data
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0(electric-pair--balance-info
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 -1 s-or-c))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (innermost (car pair-data))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (outermost (cdr pair-data)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(and=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (co= nd ((car outermost)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0(car innermost))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 ((car innermost)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0(not (eq (cdr outermost) pair)))))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ((eq syntax ?\&qu= ot;)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air--inside-string-p char))))
+ =C2=A0 =C2=A0 =C2=A0 (insert-char char)))))
+
+(defun electric-pair-default-skip-self (char)
+ =C2=A0(if electric-pair-preserve-balance
+ =C2=A0 =C2=A0 =C2=A0(electric-pair-skip-if-helps-balance char)
+ =C2=A0 =C2=A0t))
+
+(defun electric-pair-default-inhibit (char)
+ =C2=A0(if electric-pair-preserve-balance
+ =C2=A0 =C2=A0 =C2=A0(electric-pair-inhibit-if-helps-balance char)
+ =C2=A0 =C2=A0(electric-pair-conservative-inhibit char)))
+
=C2=A0(defun electric-pair-post-self-insert-function ()
=C2=A0 =C2=A0(let* ((pos (and electric-pair-mode (electric--after-char-pos)= ))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0(syntax (and pos (electric-pair-syntax last-co= mmand-event)))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 (closer (if (eq syntax ?\()
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cd= r (or (assq last-command-event electric-pair-pairs)
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(aref (syntax-table) last-command-event))) - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 last-comma= nd-event)))
- =C2=A0 =C2=A0(cond
- =C2=A0 =C2=A0 ((null pos) nil)
- =C2=A0 =C2=A0 ;; Wrap a pair around the active region.
- =C2=A0 =C2=A0 ((and (memq syntax '(?\( ?\" ?\$)) (use-region-p))=
- =C2=A0 =C2=A0 =C2=A0;; FIXME: To do this right, we'd need a post-self= -insert-function
- =C2=A0 =C2=A0 =C2=A0;; so we could add-function around it and insert the = closer after
- =C2=A0 =C2=A0 =C2=A0;; all the rest of the hook has run.
- =C2=A0 =C2=A0 =C2=A0(if (>=3D (mark) (point))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 (goto-char (mark))
- =C2=A0 =C2=A0 =C2=A0 ;; We already inserted the open-paren but at the end= of the
- =C2=A0 =C2=A0 =C2=A0 ;; region, so we have to remove it and start over. - =C2=A0 =C2=A0 =C2=A0 (delete-region (1- pos) (point))
- =C2=A0 =C2=A0 =C2=A0 (save-excursion
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char (mark))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-pair--insert last-command-eve= nt)))
- =C2=A0 =C2=A0 =C2=A0;; Since we're right after the closer now, we cou= ld tell the rest of
- =C2=A0 =C2=A0 =C2=A0;; post-self-insert-hook that we inserted `closer'= ;, but then we'd get
- =C2=A0 =C2=A0 =C2=A0;; blink-paren to kick in, which is annoying.
- =C2=A0 =C2=A0 =C2=A0;;(setq last-command-event closer)
- =C2=A0 =C2=A0 =C2=A0(insert closer))
- =C2=A0 =C2=A0 ;; Backslash-escaped: no pairing, no skipping.
- =C2=A0 =C2=A0 ((save-excursion
- =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char (1- pos))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0(not (zerop (% (skip-syntax-backward "\\&= quot;) 2))))
- =C2=A0 =C2=A0 =C2=A0nil)
- =C2=A0 =C2=A0 ;; Skip self.
- =C2=A0 =C2=A0 ((and (memq syntax '(?\) ?\" ?\$))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 electric-pair-skip-self
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (eq (char-after pos) last-command-even= t))
- =C2=A0 =C2=A0 =C2=A0;; This is too late: rather than insert&delete we= 'd want to only skip (or
- =C2=A0 =C2=A0 =C2=A0;; insert in overwrite mode). =C2=A0The difference is= in what goes in the
- =C2=A0 =C2=A0 =C2=A0;; undo-log and in the intermediate state which might= be visible to other
- =C2=A0 =C2=A0 =C2=A0;; post-self-insert-hook. =C2=A0We'll just have t= o live with it for now.
- =C2=A0 =C2=A0 =C2=A0(delete-char 1))
- =C2=A0 =C2=A0 ;; Insert matching pair.
- =C2=A0 =C2=A0 ((not (or (not (memq syntax `(?\( ?\" ?\$)))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 overwrite-mode
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (funcall electric-pair-i= nhibit-predicate last-command-event)))
- =C2=A0 =C2=A0 =C2=A0(save-excursion (electric-pair--insert closer)))))) + =C2=A0 =C2=A0 =C2=A0 =C2=A0 (skip-whitespace-info))
+ =C2=A0 =C2=A0(pcase (electric-pair-syntax-info last-command-event)
+ =C2=A0 =C2=A0 =C2=A0(`(,syntax ,pair ,unconditional ,_)
+ =C2=A0 =C2=A0 =C2=A0 (cond
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0((null pos) nil)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0;; Wrap a pair around the active region.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0((and (memq syntax '(?\( ?\) ?\" ?\$)= ) (use-region-p))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; FIXME: To do this right, we'd need a p= ost-self-insert-function
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; so we could add-function around it and ins= ert the closer after
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; all the rest of the hook has run.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (if (or (eq syntax ?\")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (and (eq syntax ?= \))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(>=3D (point) (mark)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (and (not (eq syn= tax ?\)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(>=3D (mark) (point))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (save-excursion
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (goto-char (mark))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (electric-pair--insert p= air))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (delete-region pos (1- pos))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (electric-pair--insert pair)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (goto-char (mark))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (electric-pair--insert last-command-ev= ent)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0;; Backslash-escaped: no pairing, no skipping.=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0((save-excursion
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (goto-char (1- pos))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (not (zerop (% (skip-syntax-backward &= quot;\\") 2))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 nil)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0;; Skip self.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0((and (memq syntax '(?\) ?\" ?\$)) + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(and (or unconditional + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 (if (functionp electric-pair-skip-self)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (funcall electric-pair-skip-self last-command-event)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 electric-pair-skip-self))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (save-excu= rsion
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (wh= en (setq skip-whitespace-info
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (if (functionp electric-pair-skip-wh= itespace)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (funcall electric-pair= -skip-whitespace)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 electric-pair-skip-whitespace= ))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 (electric-pair--skip-whitespace))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (eq= (char-after) last-command-event))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; This is too late: rather than insert&d= elete we'd want to only
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; skip (or insert in overwrite mode). =C2=A0= The difference is in what
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; goes in the undo-log and in the intermedia= te state which might
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; be visible to other post-self-insert-hook.= =C2=A0We'll just have to
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; live with it for now.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (when skip-whitespace-info
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (electric-pair--skip-whitespace))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (delete-region (1- pos) (if (eq skip-whitespa= ce-info 'chomp)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (point)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pos))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (forward-char))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0;; Insert matching pair.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0((and (memq syntax `(?\( ?\" ?\$))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(not overwrite-mode)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(or unconditional
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(not (funca= ll electric-pair-inhibit-predicate
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0last-command-event))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (save-excursion (electric-pair--insert pair))= )))
+ =C2=A0 =C2=A0 =C2=A0(t
+ =C2=A0 =C2=A0 =C2=A0 (when (and (if (functionp electric-pair-open-newline= -between-pairs)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(funcall electric-pair-open-newline-between-pairs)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0elec= tric-pair-open-newline-between-pairs)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(eq last-co= mmand-event ?\n)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(not (eobp)= )
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(eq (save-e= xcursion
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(skip-chars-backward "\t\s")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0(char-before (1- (point))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(matching-paren (char-after))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (save-excursion (newline 1 t)))))))
+
+(put 'electric-pair-post-self-insert-function =C2=A0 'priority =C2= =A020)

=C2=A0(defun electric-pair-will-use-region ()
=C2=A0 =C2=A0(and (use-region-p)
- =C2=A0 =C2=A0 =C2=A0 (memq (electric-pair-syntax last-command-event) '= ;(?\( ?\" ?\$))))
+ =C2=A0 =C2=A0 =C2=A0 (memq (car (electric-pair-syntax-info last-command-e= vent))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 '(?\( ?\) ?\" ?\$))))<= br>
=C2=A0;;;###autoload
=C2=A0(define-minor-mode electric-pair-mode
@@ -438,29 +827,44 @@ the mode if ARG is omitted or nil.

=C2=A0Electric Pair mode is a global minor mode. =C2=A0When enabled, typing=
=C2=A0an open parenthesis automatically inserts the corresponding
-closing parenthesis. =C2=A0\(Likewise for brackets, etc.)
-
-See options `electric-pair-pairs' and `electric-pair-skip-self'.&q= uot;
+closing parenthesis. =C2=A0\(Likewise for brackets, etc.)."
=C2=A0 =C2=A0:global t :group 'electricity
=C2=A0 =C2=A0(if electric-pair-mode
=C2=A0 =C2=A0 =C2=A0 =C2=A0(progn
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (add-hook 'post-self-insert-hook
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'electr= ic-pair-post-self-insert-function)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric--sort-post-self-insertion-hook)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 (add-hook 'self-insert-uses-region-function= s
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'electr= ic-pair-will-use-region))
=C2=A0 =C2=A0 =C2=A0(remove-hook 'post-self-insert-hook
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'electr= ic-pair-post-self-insert-function)
=C2=A0 =C2=A0 =C2=A0(remove-hook 'self-insert-uses-region-functions
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'electric-pa= ir-will-use-region)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'electric-pa= ir-will-use-region)))
+
+(defvar electric-pair-mode-map
+ =C2=A0(let ((map (make-sparse-keymap)))
+ =C2=A0 =C2=A0(define-key map [remap backward-delete-char-untabify]
+ =C2=A0 =C2=A0 =C2=A0'electric-pair-backward-delete-char-untabify)
+ =C2=A0 =C2=A0(define-key map [remap backward-delete-char]
+ =C2=A0 =C2=A0 =C2=A0'electric-pair-backward-delete-char)
+ =C2=A0 =C2=A0(define-key map [remap delete-backward-char]
+ =C2=A0 =C2=A0 =C2=A0'electric-pair-backward-delete-char)
+ =C2=A0 =C2=A0map)
+ =C2=A0"Keymap used by `electric-pair-mode'.")

=C2=A0;;; Electric newlines after/before/around some chars.

-(defvar electric-layout-rules '()
+(defvar electric-layout-rules nil
=C2=A0 =C2=A0"List of rules saying where to automatically insert newli= nes.
-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 of= no
-arguments that returns one of those symbols.")
+
+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.
+
+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.")

=C2=A0(defun electric-layout-post-self-insert-function ()
=C2=A0 =C2=A0(let* ((rule (cdr (assq last-command-event electric-layout-rul= es)))
@@ -469,23 +873,32 @@ arguments that returns one of those symbo= ls.")
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 (setq pos (electric--after-char-pos))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ;; Not in a string = or comment.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (not (nth 8 (save-e= xcursion (syntax-ppss pos)))))
- =C2=A0 =C2=A0 =C2=A0(let ((end (copy-marker (point) t)))
+ =C2=A0 =C2=A0 =C2=A0(let ((end (copy-marker (point)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(sym (if (functionp rule) (funca= ll rule) rule)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(set-marker-insertion-type end (not (eq sym &#= 39;after-stay)))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char pos)
- =C2=A0 =C2=A0 =C2=A0 =C2=A0(pcase (if (functionp rule) (funcall rule) rul= e)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(pcase sym
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; FIXME: we used `newline' do= wn here which called
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; self-insert-command and ran pos= t-self-insert-hook recursively.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; It happened to make electric-in= dent-mode work automatically with
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; electric-layout-mode (at the co= st of re-indenting lines
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; multiple times), but I'm no= t sure it's what we want.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; FIXME: check eolp before inserting \= n?
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(`before (goto-char (1- pos)) (ski= p-chars-backward " \t")
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(unless (bo= lp) (insert "\n")))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(`after =C2=A0(insert "\n")) = =C2=A0 =C2=A0 =C2=A0; FIXME: check eolp before inserting \n?
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (unless (b= olp) (insert "\n")))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(`after =C2=A0(insert "\n"))<= br> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(`after-stay (save-excursion
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 (let ((electric-layout-rules nil))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (newline 1 t))))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(`around (save-excursion
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(got= o-char (1- pos)) (skip-chars-backward " \t")
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(unl= ess (bolp) (insert "\n")))
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(insert &qu= ot;\n"))) =C2=A0 =C2=A0 =C2=A0; FIXME: check eolp before inserting \n?=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (go= to-char (1- pos)) (skip-chars-backward " \t")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (un= less (bolp) (insert "\n")))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (insert &q= uot;\n"))) =C2=A0 =C2=A0 =C2=A0; FIXME: check eolp before inserting \n= ?
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char end)))))

+(put 'electric-layout-post-self-insert-function 'priority =C2=A040= )
+
=C2=A0;;;###autoload
=C2=A0(define-minor-mode electric-layout-mode
=C2=A0 =C2=A0"Automatically insert newlines around some chars.
@@ -494,11 +907,13 @@ positive, and disable it otherwise. =C2= =A0If called from Lisp, enable
=C2=A0the mode if ARG is omitted or nil.
=C2=A0The variable `electric-layout-rules' says when and how to insert = newlines."
=C2=A0 =C2=A0:global t :group 'electricity
- =C2=A0(if electric-layout-mode
- =C2=A0 =C2=A0 =C2=A0(add-hook 'post-self-insert-hook
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0#'electric-lay= out-post-self-insert-function)
- =C2=A0 =C2=A0(remove-hook 'post-self-insert-hook
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'electric-la= yout-post-self-insert-function)))
+ =C2=A0(cond (electric-layout-mode
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (add-hook 'post-self-insert-hook
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'elec= tric-layout-post-self-insert-function)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (electric--sort-post-self-insertion-hook)) + =C2=A0 =C2=A0 =C2=A0 =C2=A0(t
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (remove-hook 'post-self-insert-hook
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0#'electric-layout-post-self-insert-function))))

=C2=A0(provide 'electric)

diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index b7bd33f..f1eae18 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -472,7 +472,13 @@ font-lock keywords will not be case sensitive."
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (font-lock-mark-block-= function . mark-defun)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (font-lock-syntactic-face-function
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0. lisp-font-lock-syntactic-face-fu= nction)))
- =C2=A0(setq-local prettify-symbols-alist lisp--prettify-symbols-alist)) + =C2=A0(setq-local prettify-symbols-alist lisp--prettify-symbols-alist) + =C2=A0;; electric
+ =C2=A0(when elisp
+ =C2=A0 =C2=A0(setq-local electric-pair-text-pairs
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(cons '(?\` . = ?\') electric-pair-text-pairs)))
+ =C2=A0(setq-local electric-pair-skip-whitespace 'chomp)
+ =C2=A0(setq-local electric-pair-open-newline-between-pairs nil))

=C2=A0(defun lisp-outline-level ()
=C2=A0 =C2=A0"Lisp mode `outline-level' function."
diff --git a/lisp/simple.el b/lisp/simple.el
index a654351..624d87f 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -610,7 +610,7 @@ In some text modes, where TAB inserts a tab, this comma= nd indents to the
=C2=A0column specified by the function `current-left-marg= in'."
=C2=A0 =C2=A0(interactive "*")
=C2=A0 =C2=A0(delete-horizontal-space t)
- =C2=A0(newline)
+ =C2=A0(newline 1 t)
=C2=A0 =C2=A0(indent-according-to-mode))

=C2=A0(defun reindent-then-newline-and-indent ()
@@ -6448,10 +6448,14 @@ More precisely, a char with closeparen syntax is se= lf-inserted.")
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(point))))))<= br> =C2=A0 =C2=A0 =C2=A0(funcall blink-paren-function)))

+(put 'blink-paren-post-self-insert-function 'priority 100)
+
=C2=A0(add-hook 'post-self-insert-hook #'blink-paren-post-self-inse= rt-function
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; Most likely, this hook is nil, = so this arg doesn't matter,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; but I use it as a reminder that= this function usually
- =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; likes to be run after others since i= t does `sit-for'.
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; likes to be run after others since i= t does
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; `sit-for'. That's also the r= eason it get a `priority' prop
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0;; of 100.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'append)

=C2=A0;; This executes C-g typed while Emacs is waiting for a command.
diff --git a/test/automated/electric-tests.el b/test/automated/electric-tes= ts.el
new file mode 100644
index 0000000..aa4a063
--- /dev/null
+++ b/test/automated/electric-tests.el
@@ -0,0 +1,509 @@
+;;; electric-tests.el --- tests for electric.el
+
+;; Copyright (C) 2013 =C2=A0Jo=C3=A3o T=C3=A1vora
+
+;; Author: Jo=C3=A3o T=C3=A1vora <joaotavora@gmail.com>
+;; Keywords:
+
+;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =C2=A0See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. =C2=A0If not, see <http://www.gnu.org/licenses/>. +
+;;; Commentary:
+
+;;
+
+;;; Code:
+(require 'ert)
+(require 'ert-x)
+(require 'electric)
+(require 'cl-lib)
+
+(defun call-with-saved-electric-modes (fn)
+ =C2=A0(let ((saved-electric (if electric-pair-mode 1 -1))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(saved-layout (if electric-layout-mode 1 -1))<= br> + =C2=A0 =C2=A0 =C2=A0 =C2=A0(saved-indent (if electric-indent-mode 1 -1)))=
+ =C2=A0 =C2=A0(electric-pair-mode -1)
+ =C2=A0 =C2=A0(electric-layout-mode -1)
+ =C2=A0 =C2=A0(electric-indent-mode -1)
+ =C2=A0 =C2=A0(unwind-protect
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(funcall fn)
+ =C2=A0 =C2=A0 =C2=A0(electric-pair-mode saved-electric)
+ =C2=A0 =C2=A0 =C2=A0(electric-indent-mode saved-indent)
+ =C2=A0 =C2=A0 =C2=A0(electric-layout-mode saved-layout))))
+
+(defmacro save-electric-modes (&rest body)
+ =C2=A0(declare (indent defun) (debug t))
+ =C2=A0`(call-with-saved-electric-modes #'(lambda () ,@body)))
+
+(defun electric-pair-test-for (fixture where char expected-string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expected-point = mode bindings fixture-fn)
+ =C2=A0(with-temp-buffer
+ =C2=A0 =C2=A0(funcall mode)
+ =C2=A0 =C2=A0(insert fixture)
+ =C2=A0 =C2=A0(save-electric-modes
+ =C2=A0 =C2=A0 =C2=A0(let ((last-command-event char))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char where)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(funcall fixture-fn)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(cl-progv
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mapcar #'car bindings)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mapcar #'cdr bindings)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(self-insert-command 1))))
+ =C2=A0 =C2=A0(should (equal (buffer-substring-no-properties (point-min) (= point-max))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expected-s= tring))
+ =C2=A0 =C2=A0(should (equal (point)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expected-p= oint))))
+
+(eval-when-compile
+ =C2=A0(defun electric-pair-define-test-form (name fixture
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0char
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0pos
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0expected-string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0expected-point
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0skip-pair-string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0prefix
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0suffix
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0extra-desc
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0mode
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0bindings
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0fixture-fn)
+ =C2=A0 =C2=A0(let* ((expected-string-and-point
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(if skip-pair-string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(with-temp-buffer<= br> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(cl-progv + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0;; FIXME: avoid `eval'
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(mapcar #'car (eval bindings))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0(mapcar #'cdr (eval bindings))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(fun= call mode)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(ins= ert fixture)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(got= o-char (1+ pos))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(ins= ert char)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(con= d ((eq (aref skip-pair-string pos)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ?p)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (insert (cadr (electric-pair-syntax-info char)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (backward-char 1))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0((eq (aref skip-pair-string pos)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ?s)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (delete-char -1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (forward-char 1)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(lis= t
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (bu= ffer-substring-no-properties (point-min) (point-max))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (po= int))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(list expected-string exp= ected-point)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (expected-string (car expected-string-= and-point))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (expected-point (cadr expected-string-= and-point))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (fixture (format "%s%s%s" pr= efix fixture suffix))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (expected-string (format "%s%s%s&= quot; prefix expected-string suffix))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (expected-point (+ (length prefix) exp= ected-point))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (pos (+ (length prefix) pos)))
+ =C2=A0 =C2=A0 =C2=A0`(ert-deftest ,(intern (format "electric-pair-%s= -at-point-%s-in-%s%s"
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1+ pos)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 extra-desc))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 ,(format "With \"%s\", try inp= ut %c at point %d. \
+Should %s \"%s\" and point at %d"
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0fixture
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0char
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(1+ pos) + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(if (string= =3D fixture expected-string)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0"stay"
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&quo= t;become")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(replace-re= gexp-in-string "\n" "\\\\n" expected-string)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0expected-po= int)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (electric-pair-test-for ,fixture
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ,(1+ pos)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ,char
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ,expected-string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ,expected-point
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ',mode
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ,bindings
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ,fixture-fn)))))
+
+(cl-defmacro define-electric-pair-test
+ =C2=A0 =C2=A0(name fixture
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0input
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0&key
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0skip-pair-string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0expected-string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0expected-point
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0bindings
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(modes '(quote (emacs-lisp-mode rub= y-mode c++-mode)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(test-in-comments t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(test-in-strings t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(test-in-code t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0(electric-pair-mode 1))))
+ =C2=A0`(progn
+ =C2=A0 =C2=A0 ,@(cl-loop
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0for mode in (eval modes) ;FIXME: avoid `eval&#= 39;
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0append
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(cl-loop
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 for (prefix suffix extra-desc) in
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (append (if test-in-comments
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 `((= ,(with-temp-buffer
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (funcall mode)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (insert "z")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (comment-region (point-min) (point-max))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 (buffer-substring-no-properties (point-min)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (1- (point-max))))<= br> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0""
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0"-in-comments")))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (if test-in-strin= gs
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 `((= "\"" "\"" "-in-strings")))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (if test-in-code<= br> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 `((= "" "" ""))))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 append
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 (cl-loop
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for char across input
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0for pos from 0
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0unless (eq char ?-)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0collect (electric-pair-define-test-form=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fixture + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 (aref inpu= t pos)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pos
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expected-s= tring
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expected-p= oint
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 skip-pair-= string
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 prefix
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 suffix
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 extra-desc=
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mode
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 bindings + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fixture-fn= ))))))
+=0C
+;;; Basic pairings and skippings
+;;;
+(define-electric-pair-test balanced-situation
+ =C2=A0" (()) =C2=A0" "(((((((" :skip-pair-string &quo= t;ppppppp"
+ =C2=A0:modes '(ruby-mode))
+
+(define-electric-pair-test too-many-openings
+ =C2=A0" ((()) " "(((((((" :skip-pair-string "ppp= pppp")
+
+(define-electric-pair-test too-many-closings
+ =C2=A0" (())) " "(((((((" :skip-pair-string "---= ---p")
+
+(define-electric-pair-test too-many-closings-2
+ =C2=A0"() =C2=A0 ) " "---(---" :skip-pair-string &quo= t;-------")
+
+(define-electric-pair-test too-many-closings-3
+ =C2=A0")() =C2=A0 =C2=A0" "(------" :skip-pair-string= "-------")
+
+(define-electric-pair-test balanced-autoskipping
+ =C2=A0" (()) =C2=A0" "---))--" :skip-pair-string &quo= t;---ss--")
+
+(define-electric-pair-test too-many-openings-autoskipping
+ =C2=A0" ((()) " "----))-" :skip-pair-string "---= ----")
+
+(define-electric-pair-test too-many-closings-autoskipping
+ =C2=A0" (())) " "---)))-" :skip-pair-string "---= sss-")
+
+=0C
+;;; Mixed parens
+;;;
+(define-electric-pair-test mixed-paren-1
+ =C2=A0" =C2=A0()] =C2=A0" "-(-(---" :skip-pair-string= "-p-p---")
+
+(define-electric-pair-test mixed-paren-2
+ =C2=A0" =C2=A0[() =C2=A0" "-(-()--" :skip-pair-string= "-p-ps--")
+
+(define-electric-pair-test mixed-paren-3
+ =C2=A0" =C2=A0(]) =C2=A0" "-(-()--" :skip-pair-string= "---ps--")
+
+(define-electric-pair-test mixed-paren-4
+ =C2=A0" =C2=A0()] =C2=A0" "---)]--" :skip-pair-string= "---ss--")
+
+(define-electric-pair-test mixed-paren-5
+ =C2=A0" =C2=A0[() =C2=A0" "----(--" :skip-pair-string= "----p--")
+
+(define-electric-pair-test find-matching-different-paren-type
+ =C2=A0" =C2=A0()] =C2=A0" "-[-----" :skip-pair-string= "-------")
+
+(define-electric-pair-test find-matching-different-paren-type-inside-list<= br> + =C2=A0"( ()]) " "-[-----" :skip-pair-string "---= ----")
+
+(define-electric-pair-test ignore-different-unmatching-paren-type
+ =C2=A0"( ()]) " "-(-----" :skip-pair-string "-p-= ----")
+
+(define-electric-pair-test autopair-keep-least-amount-of-mixed-unbalance + =C2=A0"( ()] =C2=A0" "-(-----" :skip-pair-string &quo= t;-p-----")
+
+(define-electric-pair-test dont-autopair-to-resolve-mixed-unbalance
+ =C2=A0"( ()] =C2=A0" "-[-----" :skip-pair-string &quo= t;-------")
+
+(define-electric-pair-test autopair-so-as-not-to-worsen-unbalance-situatio= n
+ =C2=A0"( (]) =C2=A0" "-[-----" :skip-pair-string &quo= t;-p-----")
+
+(define-electric-pair-test skip-over-partially-balanced
+ =C2=A0" [([]) =C2=A0 " "-----)---" :skip-pair-string = "-----s---")
+
+(define-electric-pair-test only-skip-over-at-least-partially-balanced-stuf= f
+ =C2=A0" [([()) =C2=A0" "-----))--" :skip-pair-string = "-----s---")
+
+
+
+=0C
+;;; Quotes
+;;;
+(define-electric-pair-test pair-some-quotes-skip-others
+ =C2=A0" \"\" =C2=A0 =C2=A0 =C2=A0" "-\"\&qu= ot;-----" :skip-pair-string "-ps------"
+ =C2=A0:test-in-strings nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,prog-mode-syntax-tabl= e)))
+
+(define-electric-pair-test skip-single-quotes-in-ruby-mode
+ =C2=A0" '' " "--'-" :skip-pair-string &qu= ot;--s-"
+ =C2=A0:modes '(ruby-mode)
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,prog-mode-syntax-tabl= e)))
+
+(define-electric-pair-test leave-unbalanced-quotes-alone
+ =C2=A0" \"' " "-\"'-" :skip-pair-st= ring "----"
+ =C2=A0:modes '(ruby-mode)
+ =C2=A0:test-in-strings nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,prog-mode-syntax-tabl= e)))
+
+(define-electric-pair-test leave-unbalanced-quotes-alone-2
+ =C2=A0" \"\\\"' " "-\"--'-" :s= kip-pair-string "------"
+ =C2=A0:modes '(ruby-mode)
+ =C2=A0:test-in-strings nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,prog-mode-syntax-tabl= e)))
+
+(define-electric-pair-test leave-unbalanced-quotes-alone-3
+ =C2=A0" foo\\''" "'------" :skip-pair-str= ing "-------"
+ =C2=A0:modes '(ruby-mode)
+ =C2=A0:test-in-strings nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,prog-mode-syntax-tabl= e)))
+
+(define-electric-pair-test inhibit-only-if-next-is-mismatched
+ =C2=A0"\"foo\"\"bar" "\""
+ =C2=A0:expected-string "\"\"\"foo\"\"bar&qu= ot;
+ =C2=A0:expected-point 2
+ =C2=A0:test-in-strings nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,prog-mode-syntax-tabl= e)))
+
+=0C
+;;; More quotes, but now don't bind `electric-pair-text-syntax-table&#= 39;
+;;; to `prog-mode-syntax-table'. Use the defaults for
+;;; `electric-pair-pairs' and `electric-pair-text-pairs'.
+;;;
+(define-electric-pair-test pairing-skipping-quotes-in-code
+ =C2=A0" \"\" =C2=A0 =C2=A0 =C2=A0" "-\"\&qu= ot;-----" :skip-pair-string "-ps------"
+ =C2=A0:test-in-strings nil
+ =C2=A0:test-in-comments nil)
+
+(define-electric-pair-test skipping-quotes-in-comments
+ =C2=A0" \"\" =C2=A0 =C2=A0 =C2=A0" "--\"---= --" :skip-pair-string "--s------"
+ =C2=A0:test-in-strings nil)
+
+=0C
+;;; Skipping over whitespace
+;;;
+(define-electric-pair-test whitespace-jumping
+ =C2=A0" ( =C2=A0 =C2=A0) =C2=A0" "--))))---" :expecte= d-string " ( =C2=A0 =C2=A0) =C2=A0" :expected-point 8
+ =C2=A0:bindings '((electric-pair-skip-whitespace . t)))
+
+(define-electric-pair-test whitespace-chomping
+ =C2=A0" ( =C2=A0 =C2=A0) =C2=A0" "--)------" :expecte= d-string " () =C2=A0" :expected-point 4
+ =C2=A0:bindings '((electric-pair-skip-whitespace . chomp)))
+
+(define-electric-pair-test whitespace-chomping-2
+ =C2=A0" ( \n\t\t\n =C2=A0) =C2=A0" "--)------" :expec= ted-string " () =C2=A0" :expected-point 4
+ =C2=A0:bindings '((electric-pair-skip-whitespace . chomp))
+ =C2=A0:test-in-comments nil)
+
+(define-electric-pair-test whitespace-chomping-dont-cross-comments
+ =C2=A0" ( \n\t\t\n =C2=A0) =C2=A0" "--)------" :expec= ted-string " () \n\t\t\n =C2=A0) =C2=A0"
+ =C2=A0:expected-point 4
+ =C2=A0:bindings '((electric-pair-skip-whitespace . chomp))
+ =C2=A0:test-in-strings nil
+ =C2=A0:test-in-code nil
+ =C2=A0:test-in-comments t)
+
+=0C
+;;; Pairing arbitrary characters
+;;;
+(define-electric-pair-test angle-brackets-everywhere
+ =C2=A0"<>" "<>" :skip-pair-string "p= s"
+ =C2=A0:bindings '((electric-pair-pairs . ((?\< . ?\>)))))
+
+(define-electric-pair-test angle-brackets-everywhere-2
+ =C2=A0"(<>" "-<>" :skip-pair-string "= ;-ps"
+ =C2=A0:bindings '((electric-pair-pairs . ((?\< . ?\>)))))
+
+(defvar electric-pair-test-angle-brackets-table
+ =C2=A0(let ((table (make-syntax-table prog-mode-syntax-table)))
+ =C2=A0 =C2=A0(modify-syntax-entry ?\< "(>" table)
+ =C2=A0 =C2=A0(modify-syntax-entry ?\> ")<`" table)
+ =C2=A0 =C2=A0table))
+
+(define-electric-pair-test angle-brackets-pair
+ =C2=A0"<>" "<" :expected-string "<&g= t;<>" :expected-point 2
+ =C2=A0:test-in-code nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,electric-pair-test-an= gle-brackets-table)))
+
+(define-electric-pair-test angle-brackets-skip
+ =C2=A0"<>" "->" :expected-string "<&= gt;" :expected-point 3
+ =C2=A0:test-in-code nil
+ =C2=A0:bindings `((electric-pair-text-syntax-table
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 . ,electric-pair-test-an= gle-brackets-table)))
+
+(define-electric-pair-test pair-backtick-and-quote-in-comments
+ =C2=A0";; " "---`" :expected-string ";; `'&q= uot; :expected-point 5
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:modes '(emacs-lisp-mode)
+ =C2=A0:bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) +
+(define-electric-pair-test skip-backtick-and-quote-in-comments
+ =C2=A0";; `foo'" "-------'" :expected-string = ";; `foo'" :expected-point 9
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:modes '(emacs-lisp-mode)
+ =C2=A0:bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) +
+(define-electric-pair-test pair-backtick-and-quote-in-strings
+ =C2=A0"\"\"" "-`" :expected-string "\&= quot;`'\"" :expected-point 3
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:modes '(emacs-lisp-mode)
+ =C2=A0:bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) +
+(define-electric-pair-test skip-backtick-and-quote-in-strings
+ =C2=A0"\"`'\"" "--'" :expected-stri= ng "\"`'\"" :expected-point 4
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:modes '(emacs-lisp-mode)
+ =C2=A0:bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) +
+(define-electric-pair-test skip-backtick-and-quote-in-strings-2
+ =C2=A0" =C2=A0\"`'\"" "----'" :expe= cted-string " =C2=A0\"`'\"" :expected-point 6
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:modes '(emacs-lisp-mode)
+ =C2=A0:bindings '((electric-pair-text-pairs . ((?\` . ?\'))))) +
+=0C
+;;; `js-mode' has `electric-layout-rules' for '{ and '} +;;;
+(define-electric-pair-test js-mode-braces
+ =C2=A0"" "{" :expected-string "{}" :expecte= d-point 2
+ =C2=A0:modes '(js-mode)
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)))
+
+(define-electric-pair-test js-mode-braces-with-layout
+ =C2=A0"" "{" :expected-string "{\n\n}" :exp= ected-point 3
+ =C2=A0:modes '(js-mode)
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-l= ayout-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)))
+
+(define-electric-pair-test js-mode-braces-with-layout-and-indent
+ =C2=A0"" "{" :expected-string "{\n =C2=A0 =C2=A0= \n}" :expected-point 7
+ =C2=A0:modes '(js-mode)
+ =C2=A0:test-in-comments nil
+ =C2=A0:test-in-strings nil
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-i= ndent-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-l= ayout-mode 1)))
+
+=0C
+;;; Backspacing
+;;; TODO: better tests
+;;;
+(ert-deftest electric-pair-backspace-1 ()
+ =C2=A0(save-electric-modes
+ =C2=A0 =C2=A0(with-temp-buffer
+ =C2=A0 =C2=A0 =C2=A0(insert "()")
+ =C2=A0 =C2=A0 =C2=A0(goto-char 2)
+ =C2=A0 =C2=A0 =C2=A0(electric-pair-backward-delete-char 1)
+ =C2=A0 =C2=A0 =C2=A0(should (equal "" (buffer-string))))))
+
+=0C
+;;; Electric newlines between pairs
+;;; TODO: better tests
+(ert-deftest electric-pair-open-extra-newline ()
+ =C2=A0(save-electric-modes
+ =C2=A0 =C2=A0(with-temp-buffer
+ =C2=A0 =C2=A0 =C2=A0(c-mode)
+ =C2=A0 =C2=A0 =C2=A0(electric-pair-mode 1)
+ =C2=A0 =C2=A0 =C2=A0(electric-indent-mode 1)
+ =C2=A0 =C2=A0 =C2=A0(insert "int main {}")
+ =C2=A0 =C2=A0 =C2=A0(backward-char 1)
+ =C2=A0 =C2=A0 =C2=A0(let ((c-basic-offset 4))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(newline 1 t)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(should (equal "int main {\n =C2=A0 =C2= =A0\n}"
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 (buffer-string)))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0(should (equal (point) (- (point-max) 2)))))))=
+
+
+=0C
+;;; Autowrapping
+;;;
+(define-electric-pair-test autowrapping-1
+ =C2=A0"foo" "(" :expected-string "(foo)" :e= xpected-point 2
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mark-sexp = 1)))
+
+(define-electric-pair-test autowrapping-2
+ =C2=A0"foo" ")" :expected-string "(foo)" :e= xpected-point 6
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mark-sexp = 1)))
+
+(define-electric-pair-test autowrapping-3
+ =C2=A0"foo" ")" :expected-string "(foo)" :e= xpected-point 6
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char = (point-max))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(skip-chars= -backward "\"")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mark-sexp = -1)))
+
+(define-electric-pair-test autowrapping-4
+ =C2=A0"foo" "(" :expected-string "(foo)" :e= xpected-point 2
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char = (point-max))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(skip-chars= -backward "\"")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mark-sexp = -1)))
+
+(define-electric-pair-test autowrapping-5
+ =C2=A0"foo" "\"" :expected-string "\"f= oo\"" :expected-point 2
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mark-sexp = 1)))
+
+(define-electric-pair-test autowrapping-6
+ =C2=A0"foo" "\"" :expected-string "\"f= oo\"" :expected-point 6
+ =C2=A0:fixture-fn #'(lambda ()
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(electric-p= air-mode 1)
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(goto-char = (point-max))
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(skip-chars= -backward "\"")
+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(mark-sexp = -1)))
+
+(provide 'electric-pair-tests)
+;;; electric-pair-tests.el ends here


--089e015383e41aee8b04ee489231--