AutoPairs

Difference between revision 62 and current revision

Summary: Rollback to 2013-02-14 20:58 UTC

No diff available.

It can be useful to insert parentheses, braces, quotes and the like in matching pairs – e.g., pressing “(” inserts ‘()’, with the cursor in between. In the TextMate editor, this is called “auto-paired characters”.

Implementations

electric-pair-mode

Emacs-24 comes with a new simple global minor mode electric-pair-mode which uses the new post-self-insert-hook.

autopair.el

http://github.com/capitaomorte/autopair (download) is a minor-mode that works with almost all major modes. This is probably most likely the easiest (and best) way to achieve autopairs functionality in all your favorite modes.

Key Points:

How does this work?

Does it make use of command loop hooks? Well the answer to the question “how” is “read the source code”, but in short, yes 😊, post-command-hook is used. Here’s is a snippet of the binding for an opening brace/parenthesis/whatever

   (defun autopair-insert-opening ()
     (interactive)
     (when (autopair-pair-p)
       (setq autopair-action (list 'opening (autopair-find-pair) (point))))
     (autopair-fallback))

autopair-fallback takes care of running the original binding. When running the post-command-hook one reads autopair-action and decides what to do. – JoaoTavora?

Using with paredit-mode

Autopair doesn’t make much sense when paredit-mode is turned on, so it actually defers to paredit-mode when that is installed and enabled. Using autopair-global-mode is thus safe but anyway the following code sample turns on autopairs for the modes listed in autopair-modes, but disables it when paredit-mode is turned on:

  (require 'autopair)

  (defvar autopair-modes '(r-mode ruby-mode))
  (defun turn-on-autopair-mode () (autopair-mode 1))
  (dolist (mode autopair-modes) (add-hook (intern (concat (symbol-name mode) "-hook")) 'turn-on-autopair-mode))

  (require 'paredit)
  (defadvice paredit-mode (around disable-autopairs-around (arg))
    "Disable autopairs mode if paredit-mode is turned on"
    ad-do-it
    (if (null ad-return-value)
        (autopair-mode 1)
      (autopair-mode 0)
      ))

  (ad-activate 'paredit-mode)

TimCHarper (tweaked by JoaoTavora?)

Extending with auto-pairs+.el

Auto-pairs+.el extends Autopair functionality as follows:

For example selecting the following string:

This is a test of the quoting system, "this is only a test"

And pressing quote, gives:

"This is a test of the quoting system, \"this is only a test\""
 Quoting the whole phrase again gives:
"\"This is a test of the quoting system, \"this is only a test\"\""

You may also select a region within the string and quote it. When this happens, the appropriate escape characters are used. For example by selecting the word quoting and pressing “ in emacs-lisp-mode gives:

 "\"This is a test of the \"quoting\" system, \"this is only a test\"\""

Quotes are also automatically turned into escaped quotes when in strings.

See auto-pair+.el

Also check out some bug-fixes and the new variable autopair-skip-whitespace-but-dont-delete.

MatthewFidler

hi, I use your AutoPairs, its a good tool.

1. can you add pair “`’” in emacs-lisp-mode to AutoPairs, its so common in emacs-lisp-mode.

2. After I bind “C-h”(I bind “C-x /” to help-command) to autopair-backspace, when I type “C-h”, I get one error message “Lisp nesting exceeds ‘max-lisp-eval-depth’

3.

 (setq a <cursor here> bcd")
or
 (setq a '<cursor here>abc))

this example, when I type double quotes or paren, it became this:

 (setq a ""bcd")
or
(setq a '()abc))

do you have a good way to solve it?

3x – ahei

I don’t think the wiki is the best place to ask questions. However, why aren’t you using paredit mode in common-lisp-mode? I would disable autopairs and use paredit mode instead.

um, how do I report bug to you? email? I also tried paredit before tried autopairs, but I think autopairs is better than paredit, 😊, only I think, I think there are more bugs in paredit, so I choose autopairs. That’s the bad news for me you’ll disable autopairs, 😟. – ahei

There is a new feature that implements your desired backtick-and-quote custom pairing. Anyway to submit a bug or track autopair.el development go to http://github.com/capitaomorte/autopair, you can ask questions there and I can follow-up much better on them. For example one of the issues in point 3 you report is a not a bug, autopair can’t guess you don’t mean to start a string before the “)”.--JoaoTavora?

About backtick-and-quote, I already used it, it’s good. Small suggestion, I think “(setq autopair-extra-pairs `(:everywhere ((?` . ?'))))” is more common for elisp users.

 -- [[ahei]]

smartparens.el

Smartparens is minor mode for Emacs that deals with parens pairs and tries to be smart about it. It is a unification and enhancement effort to combine funcitonality of several existing packages in a single, common and straightforward way (and most of all compatible). These packages include autopair, textmate, wrap-region, paredit and others with similar philosophies. It also adds support for many more features. Here’s a highlight of some features, for a complete list and detailed documentation look at the project wiki.

 -- Fuco

SkeletonMode

The Skeleton library provides pair insertion via the skeleton-insert-maybe command, which can be bound to the opening key:

    (setq skeleton-pair t)
    (global-set-key "(" 'skeleton-pair-insert-maybe)
    (global-set-key "[" 'skeleton-pair-insert-maybe)
    (global-set-key "{" 'skeleton-pair-insert-maybe)

The skeleton-pair variable must be non-nil for pairing to occur. Pairing is possible with all characters. By default, ‘(’, ‘[’, ‘{’, ‘<’ and ‘`’ all pair with the symmetrical character. This behavior can be modified by the variable skeleton-pair-alist.

Skeletons also work with selections – typing the opening character will wrap the selected text in the opening and closing characters.

Filtering

By default, pairing will not be done if the following character is part of a word (skeleton-pair-on-word). To limit pairing further, the variable skeleton-pair-filter-function points to a function which determines whether pairing is appropriate. If the function returns nil, paired insertion is attempted; otherwise, paired insertion will not be performed.

For example,

    (setq skeleton-pair-filter-function
          '(lambda ()
             (cond
              ((eq last-command-char ?\")
               (or (looking-at   (regexp-quote (string last-command-char)))
                   (looking-back (regexp-quote (string last-command-char)))
                   (looking-back "[[:graph:]]")))
              (t
               (looking-at (regexp-quote (string last-command-char)))))))

The default clause above (t) means ‘()’ will only be inserted if the cursor is not followed by ‘(’ in the text. See the documentation of skeleton-pair-insert-maybe (C-h f skeleton-pair-insert-maybe) for more information.

TextMate.el

textmate.el extends the pairing behavior: the cursor “moves over” closing characters instead of inserting new ones, and empty pairs like ‘()’ are deleted with a single keypress. ParEdit, a minor mode for Lisp-like languages, also provides both these features.

As a fall-back, you may place the (more general) code below in .emacs:

    (setq skeleton-pair t)
    (setq skeleton-pair-alist
          '((?\( _ ?\))
            (?[  _ ?])
            (?{  _ ?})
            (?\" _ ?\")))
    (defun autopair-insert (arg)
      (interactive "P")
      (let (pair)
        (cond
         ((assq last-command-char skeleton-pair-alist)
          (autopair-open arg))
         (t
          (autopair-close arg)))))
    (defun autopair-open (arg)
      (interactive "P")
      (let ((pair (assq last-command-char
                        skeleton-pair-alist)))
        (cond
         ((and (not mark-active)
               (eq (car pair) (car (last pair)))
               (eq (car pair) (char-after)))
          (autopair-close arg))
         (t
          (skeleton-pair-insert-maybe arg)))))
    (defun autopair-close (arg)
      (interactive "P")
      (cond
       (mark-active
        (let (pair open)
          (dolist (pair skeleton-pair-alist)
            (when (eq last-command-char (car (last pair)))
              (setq open (car pair))))
          (setq last-command-char open)
          (skeleton-pair-insert-maybe arg)))
       ((looking-at
         (concat "[ \t\n]*"
                 (regexp-quote (string last-command-char))))
        (replace-match (string last-command-char))
        (indent-according-to-mode))
       (t
        (self-insert-command (prefix-numeric-value arg))
        (indent-according-to-mode))))
    (defadvice delete-backward-char (before autopair activate)
      (when (and (char-after)
                 (eq this-command 'delete-backward-char)
                 (eq (char-after)
                     (car (last (assq (char-before) skeleton-pair-alist)))))
        (delete-char 1)))
    (global-set-key "("  'autopair-insert)
    (global-set-key ")"  'autopair-insert)
    (global-set-key "["  'autopair-insert)
    (global-set-key "]"  'autopair-insert)
    (global-set-key "{"  'autopair-insert)
    (global-set-key "}"  'autopair-insert)
    (global-set-key "\"" 'autopair-insert)
    ;; ...

The autopair-insert command is bound to both the opening and closing keys. To add a new pair, extend skeleton-pair-alist as such:

    (add-to-list 'skeleton-pair-alist '(?<  _ ?>))

and bind autopair-insert to the necessary key(s).

The above code has been made into a package here. It has a few neat little extras such as being able to turn off the pairing of some characters in some modes.

CC Mode

To use these commands in CC Mode:

    (defun autopair-close-block (arg)
      (interactive "P")
      (cond
       (mark-active
        (autopair-close arg))
       ((not (looking-back "^[[:space:]]*"))
        (newline-and-indent)
        (autopair-close arg))
       (t
        (autopair-close arg))))
    (add-hook 'c-mode-common-hook
              '(lambda ()
                 (local-set-key "(" 'autopair-insert)
                 (local-set-key ")" 'autopair-insert)
                 (local-set-key "{" 'autopair-insert)
                 (local-set-key "}" 'autopair-close-block)))

The autopair-close-block command ensures that }’s are placed on separate lines.

Electric RET

Short video demonstration

Similar to the “electric” behavior of CC Mode, RET can be made to place an empty line between the opening and closing characters:

    (defun autopairs-ret (arg)
      (interactive "P")
      (let (pair)
        (dolist (pair skeleton-pair-alist)
          (when (eq (char-after) (car (last pair)))
            (save-excursion (newline-and-indent))))
        (newline arg)
        (indent-according-to-mode)))
    (global-set-key (kbd "RET") 'autopairs-ret)

Why is this useful? Watch the little screencast above. The RET key “opens” parenthetical expressions while editing; the ) key “closes” them. This is particularly convenient when writing Lisp code, as it facilitates line-wise editing without violating good code style.

Tips and tricks

To quickly “jump out of” a matched pair and to the end of the line, type ‘C-e’. See OpenNextLine to “jump” onto a new line.

See also

Caio Marcelo’s blog post A little Emacs experiment pieces together a similar system in a very educational manner.

The SkeletonMode page explores skeletons more broadly.

ParEdit is a minor mode for Lisp languages, and provides enhanced sexp editing, navigating and manipulating. It does all of the above and more (but targets Lisp).

Discussion

Would it be possible to make a skeleton pair that would automatically match a ‘\(’ with a ‘\)’? The same should be true for ‘\[’ and ‘\]’. Thanks!

Here’s two skeletons made with define-skeleton:

    (define-skeleton quoted-parentheses
      "Insert \\( ... \\)."
      nil "\\(" _ "\\)")
    (define-skeleton quoted-brackets
      "Insert \\[ ... \\]."
      nil "\\[" _ "\\]")

Then bind them as you see fit, e.g.,

    (global-set-key "\C-c(" 'quoted-parentheses)
    (global-set-key "\C-c[" 'quoted-brackets)

Hope this helps!

That works great! Thanks. I was wondering if it can be bind to \( without interfering with \. I.e., insert a ‘\’ when I type it. If I happen to type a ‘(’ afterward, insert a matching ‘\)’. The same for ‘\’ and ‘[’.

The way I set it up ‘\’ is captured so that it wont insert a plain ‘\’.

Thanks.

In that case, you’re better off defining the commands with defun.

    (defun quoted-parentheses (arg)
      (interactive "P")
      (if (looking-back "\\\\")
          (skeleton-insert '(nil "(" _ "\\)"))
        (self-insert-command arg)))
    (defun quoted-brackets (arg)
      (interactive "P")
      (if (looking-back "\\\\")
          (skeleton-insert '(nil "[" _ "\\]"))
        (self-insert-command arg)))
    (global-set-key "(" 'quoted-parentheses)
    (global-set-key "[" 'quoted-brackets)

Also, remember that no matter what you do, you can always get the “plain” behavior by pressing ‘C-q’ first. E.g., ‘C-q \’ will always produce a single backslash.

This works great 😊 I hardly dare ask, but … this breaks the lovely skeleton-setup of matching ‘(’ and ‘)’. [1] Is it possible to combine these two features? I appreciate this help. I really enjoy working with Emacs, but programming is certainly not my strength. Thanks!

Simply replace self-insert-command with skeleton-pair-insert-maybe in the function definitions, like so:

    (setq skeleton-pair t) ; enable pairing
    (defun quoted-parentheses (arg)
      (interactive "P")
      (if (looking-back "\\\\")
          (skeleton-insert '(nil "(" _ "\\)") -1)
        (skeleton-pair-insert-maybe arg)))
    (defun quoted-brackets (arg)
      (interactive "P")
      (if (looking-back "\\\\")
          (skeleton-insert '(nil "[" _ "\\]") -1)
        (skeleton-pair-insert-maybe arg)))
    (global-set-key "(" 'quoted-parentheses)
    (global-set-key "[" 'quoted-brackets)

This will give ‘()’ in regular situations and ‘\(\)’ when point is preceded by a backslash.

Perfect! This should be included in AUCTeX as default! Should we clean up the discussion or just leave it as it is? Thanks again!

This does not work on my emacs version. Even with a .emacs file which contains only the code above, I get the message “Marker does not point anywhere”. I am also a very novice elisp programmer.

Since this is more of an AutoPairs issue than an AUCTeX issue, I’m moving the discussion here. Do check out the other code on this page. 😊

Hmm. The AUCTeX page points back to the present one.

Maybe it is an AutoPairs issue 😊 The info is relevant to OrgMode as well.

It can actually be improved further, but I have no idea how to do it.

First of all it might be nice if one can embrace a region with \( .. \). With the implementation above it does not seem possible. Maybe one just need to bind the quoted-parentheses/quoted-brackets to some other key?

Second, when deleting it would be nice if pairs were deleted together. I.e. if I delete \) the matching \( should be deleted.

Thanks,


CategoryCode CategoryTemplates CategoryParentheses ) (skeleton-insert ‘(nil extends the pairing behavior: the cursor _