emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] trunk r115759: Make Electric Pair mode smarter/more useful


From: João Távora
Subject: [Emacs-diffs] trunk r115759: Make Electric Pair mode smarter/more useful:
Date: Thu, 26 Dec 2013 22:12:35 +0000
User-agent: Bazaar (2.6b2)

------------------------------------------------------------
revno: 115759
revision-id: address@hidden
parent: address@hidden
committer: João Távora <address@hidden>
branch nick: trunk
timestamp: Thu 2013-12-26 22:02:49 +0000
message:
  Make Electric Pair mode smarter/more useful:
  
  * lisp/electric.el: Pairing/skipping helps preserve
    balance. Autobackspacing behaviour. Opens extra newlines between
    pairs. Skip whitespace before closing delimiters.
  
  * lisp/emacs-lisp/lisp-mode.el (lisp-mode-variables): Use new
    features.
  
  * test/automated/electric-tests.lisp: New file.
  
  * doc/emacs/programs.texi: Describe new features.
  
  * lisp/simple.el: Pass non-nil interactive arg to newline call inside
    newline-and-indent.
added:
  test/automated/electric-tests.el 
electrictests.el-20131226202454-s3cqbs9maop0w8qy-1
modified:
  doc/emacs/ChangeLog            changelog-20091113204419-o5vbwnq5f7feedwu-6227
  doc/emacs/programs.texi        
programs.texi-20091113204419-o5vbwnq5f7feedwu-6272
  etc/ChangeLog                  changelog-20091113204419-o5vbwnq5f7feedwu-1485
  etc/NEWS                       news-20100311060928-aoit31wvzf25yr1z-1
  lisp/ChangeLog                 changelog-20091113204419-o5vbwnq5f7feedwu-1432
  lisp/electric.el               electric.el-20091113204419-o5vbwnq5f7feedwu-47
  lisp/emacs-lisp/lisp-mode.el   lispmode.el-20091113204419-o5vbwnq5f7feedwu-205
  lisp/simple.el                 simple.el-20091113204419-o5vbwnq5f7feedwu-403
  test/ChangeLog                 changelog-20091113204419-o5vbwnq5f7feedwu-8588
=== modified file 'doc/emacs/ChangeLog'
--- a/doc/emacs/ChangeLog       2013-12-25 10:24:52 +0000
+++ b/doc/emacs/ChangeLog       2013-12-26 22:02:49 +0000
@@ -1,3 +1,7 @@
+2013-12-26  João Távora <address@hidden>
+       * emacs.texi (Matching): Describe new features of Electric Pair
+       mode.
+
 2013-12-25  Chong Yidong  <address@hidden>
 
        * glossary.texi (Glossary): Define MULE in modern terms.

=== modified file 'doc/emacs/programs.texi'
--- a/doc/emacs/programs.texi   2013-07-02 07:42:34 +0000
+++ b/doc/emacs/programs.texi   2013-12-26 22:02:49 +0000
@@ -844,8 +844,34 @@
   Electric Pair mode, a global minor mode, provides a way to easily
 insert matching delimiters.  Whenever you insert an opening delimiter,
 the matching closing delimiter is automatically inserted as well,
-leaving point between the two.  To toggle Electric Pair mode, type
address@hidden electric-pair-mode}.
+leaving point between the two. Conversely, when you insert a closing
+delimiter over an existing one, no inserting takes places and that
+position is simply skipped over. These variables control additional
+features of Electric Pair mode:
+
address@hidden @bullet
address@hidden
address@hidden, when address@hidden, makes the
+default pairing logic balance out the number of opening and closing
+delimiters.
+
address@hidden
address@hidden, when address@hidden, makes
+backspacing between two adjacent delimiters also automatically delete
+the closing delimiter.
+
address@hidden
address@hidden, when address@hidden,
+makes inserting inserting a newline between two adjacent pairs also
+automatically open and extra newline after point.
+
address@hidden
address@hidden, when address@hidden, causes the minor
+mode to skip whitespace forward before deciding whether to skip over
+the closing delimiter.
address@hidden itemize
+
+To toggle Electric Pair mode, type @kbd{M-x electric-pair-mode}.
 
 @node Comments
 @section Manipulating Comments

=== modified file 'etc/ChangeLog'
--- a/etc/ChangeLog     2013-12-23 22:27:49 +0000
+++ b/etc/ChangeLog     2013-12-26 22:02:49 +0000
@@ -1,3 +1,6 @@
+2013-12-26  João Távora <address@hidden>
+       * NEWS: Describe new features of Electric Pair mode.
+
 2013-12-23  Teodor Zlatanov  <address@hidden>
 
        * NEWS: Updated for `gnutls-verify-error', cfengine-mode, and

=== modified file 'etc/NEWS'
--- a/etc/NEWS  2013-12-26 03:27:45 +0000
+++ b/etc/NEWS  2013-12-26 22:02:49 +0000
@@ -439,6 +439,42 @@
 
 ** Eldoc Mode works properly in the minibuffer.
 
+** Electric Pair mode
+*** New `electric-pair-preserve-balance' enabled by default.
+
+Pairing/skipping only kicks in when that help the balance of
+parentheses and quotes, i.e. the buffer should end up at least as
+balanced as before.
+
+You can further control this behaviour by adjusting the predicates
+stored in `electric-pair-inhibit-predicate' and
+`electric-pair-skip-self'.
+
+*** New `electric-pair-delete-adjacent-pairs' enabled by default.
+
+In `electric-pair-mode', the commands `backward-delete-char' and
+`backward-delete-char-untabify' are now bound to electric variants
+that delete the closer when invoked between adjacent pairs.
+
+*** New `electric-pair-open-newline-between-pairs' enabled by default.
+
+In `electric-pair-mode', inserting a newline between adjacent pairs
+opens an extra newline after point, which is indented if
+`electric-indent-mode' is also set.
+
+*** New `electric-pair-skip-whitespace' enabled by default.
+
+Controls if skipping over closing delimiters should jump over any
+whitespace slack. Setting it to `chomp' makes it delete this
+whitespace. See also the variable
+`electric-pair-skip-whitespace-chars'.
+
+*** New variables control the pairing in strings and comments.
+
+You can customize `electric-pair-text-pairs' and
+`electric-pair-text-syntax-table' to tweak pairing behaviour inside
+strings and comments.
+
 ** EPA
 
 *** New option `epa-mail-aliases'.

=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog    2013-12-26 16:45:19 +0000
+++ b/lisp/ChangeLog    2013-12-26 22:02:49 +0000
@@ -1,3 +1,25 @@
+2013-12-26  João Távora <address@hidden>
+
+       * 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-26  Fabián Ezequiel Gallina  <address@hidden>
 
        * progmodes/python.el: Use lexical-binding.

=== modified file 'lisp/electric.el'
--- a/lisp/electric.el  2013-11-29 17:59:38 +0000
+++ b/lisp/electric.el  2013-12-26 22:02:49 +0000
@@ -187,6 +187,17 @@
                            (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 @@
                    (> 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 @@
                      #'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 @@
 
 (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 @@
    ;; 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,297 @@
        (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))
+             '(?\( ?\) ?\" ?\$))))
+
+(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'.")
 
 ;;;###autoload
 (define-minor-mode electric-pair-mode
@@ -438,29 +838,33 @@
 
 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)))
 
 ;;; 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 @@
                (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 @@
 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)
 

=== modified file 'lisp/emacs-lisp/lisp-mode.el'
--- a/lisp/emacs-lisp/lisp-mode.el      2013-12-19 21:02:46 +0000
+++ b/lisp/emacs-lisp/lisp-mode.el      2013-12-26 22:02:49 +0000
@@ -472,7 +472,13 @@
          (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."

=== modified file 'lisp/simple.el'
--- a/lisp/simple.el    2013-12-23 03:59:10 +0000
+++ b/lisp/simple.el    2013-12-26 22:02:49 +0000
@@ -610,7 +610,7 @@
 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 @@
                                 (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.

=== modified file 'test/ChangeLog'
--- a/test/ChangeLog    2013-12-25 18:07:31 +0000
+++ b/test/ChangeLog    2013-12-26 22:02:49 +0000
@@ -1,3 +1,6 @@
+2013-12-26  João Távora <address@hidden>
+       * automated/electric-tests.el: Add tests for Electric Pair mode.
+
 2013-12-25  Fabián Ezequiel Gallina  <address@hidden>
 
        * automated/python-tests.el

=== added file 'test/automated/electric-tests.el'
--- a/test/automated/electric-tests.el  1970-01-01 00:00:00 +0000
+++ b/test/automated/electric-tests.el  2013-12-26 22:02:49 +0000
@@ -0,0 +1,510 @@
+;;; electric-tests.el --- tests for electric.el
+
+;; Copyright (C) 2013  João Távora
+
+;; Author: João Távora <address@hidden>
+;; 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 <http://www.gnu.org/licenses/>.
+
+;;; Commentary: Tests for Electric Pair mode.
+;;; TODO: Add tests for other Electric-* functionality
+
+;;
+
+;;; 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-tests)
+;;; electric-tests.el ends here


reply via email to

[Prev in Thread] Current Thread [Next in Thread]