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ão Távora 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 > `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ão > > 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ão Távora > + > + * 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 quotes > + 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 and > + 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-function > - (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 ) results > 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 inside > +quotes or comments. If lookup fails here, `electric-pair-text-pairs' will > +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 is > +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-event) > + 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' 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))) > + (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 (>= (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 the > - ;; undo-log and in the intermediate state which might be visible to > 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-function > + ;; so we could add-function around it and insert the closer after > + ;; all the rest of the hook has run. > + (if (or (eq syntax ?\") > + (and (eq syntax ?\)) > + (>= (point) (mark))) > + (and (not (eq syntax ?\))) > + (>= (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 only > + ;; skip (or insert in overwrite mode). The difference is in what > + ;; goes in the undo-log and in the intermediate state which might > + ;; 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-pairs) > + (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ão Távora > + > +;; Author: João Távora > +;; 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= 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-list > + "( ()]) " "-[-----" :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-point > 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 > >