Emacs: Insert Brackets by Pair

By Xah Lee. Date: . Last updated: .

This page shows a command xah-insert-bracket-pair to insert bracket pair, better than the emacs builtin electric-pair-mode .

Emacs has electric-pair-mode , which insert a bracket pair when you type the left bracket. 〔►see Emacs: Insert Parenthesis by Pair: electric-pair-mode

Problems of electric-pair-mode

  1. It does not close { } when in emacs-lisp-mode (as of 2017-01-17, GNU Emacs 25.1.1). Which brackets it will close depends on the major mode you are in.
  2. When you have text selection, it doesn't wrap the bracket around it.
  3. The keys to insert brackets, are still the keys on PC keyboard, which are typed by your stretched pinky.

Advantages of xah-insert-bracket-pair

  1. Wrap brackets around current word (if cursor is on a word), or text selection, else just insert a pair and place cursor in between.
  2. Behavior is same and predictable anywhere.
  3. Convient shortcuts keys on homerow, or any key you chose.

Bracket Pair Insertion Commands

Put this in your emacs init.

(defun xah-insert-bracket-pair (*left-bracket *right-bracket &optional *wrap-method)
  "Insert brackets around selection, word, at point, and maybe move cursor in between.

 *left-bracket and *right-bracket are strings. *wrap-method must be either 'line or 'block. 'block means between empty lines.

• if there's a region, add brackets around region.
• If *wrap-method is 'line, wrap around line.
• If *wrap-method is 'block, wrap around block.
• if cursor is at beginning of line and its not empty line and contain at least 1 space, wrap around the line.
• If cursor is at end of a word or buffer, one of the following will happen:
 xyz▮ → xyz(▮)
 xyz▮ → (xyz▮)       if in one of the lisp modes.
• wrap brackets around word if any. e.g. xy▮z → (xyz▮). Or just (▮)

URL `http://ergoemacs.org/emacs/elisp_insert_brackets_by_pair.html'
Version 2017-01-17"
  (if (use-region-p)
      (progn ; there's active region
        (let (
              ($p1 (region-beginning))
              ($p2 (region-end)))
          (goto-char $p2)
          (insert *right-bracket)
          (goto-char $p1)
          (insert *left-bracket)
          (goto-char (+ $p2 2))))
    (progn ; no text selection
      (let ($p1 $p2)
         ((eq *wrap-method 'line)
          (setq $p1 (line-beginning-position) $p2 (line-end-position))
          (goto-char $p2)
          (insert *right-bracket)
          (goto-char $p1)
          (insert *left-bracket)
          (goto-char (+ $p2 (length *left-bracket))))
         ((eq *wrap-method 'block)
              (if (re-search-backward "\n[ \t]*\n" nil 'move)
                  (progn (re-search-forward "\n[ \t]*\n")
                         (setq $p1 (point)))
                (setq $p1 (point)))
              (if (re-search-forward "\n[ \t]*\n" nil 'move)
                  (progn (re-search-backward "\n[ \t]*\n")
                         (setq $p2 (point)))
                (setq $p2 (point))))
            (goto-char $p2)
            (insert *right-bracket)
            (goto-char $p1)
            (insert *left-bracket)
            (goto-char (+ $p2 (length *left-bracket)))))
         ( ;  do line. line must contain space
           (eq (point) (line-beginning-position))
           ;; (string-match " " (buffer-substring-no-properties (line-beginning-position) (line-end-position)))
           (not (eq (line-beginning-position) (line-end-position))))
          (insert *left-bracket )
          (insert  *right-bracket))
           (or ; cursor is at end of word or buffer. i.e. xyz▮
            (looking-at "[^-_[:alnum:]]")
            (eq (point) (point-max)))
           (not (or
                 (string-equal major-mode "xah-elisp-mode")
                 (string-equal major-mode "emacs-lisp-mode")
                 (string-equal major-mode "lisp-mode")
                 (string-equal major-mode "lisp-interaction-mode")
                 (string-equal major-mode "common-lisp-mode")
                 (string-equal major-mode "clojure-mode")
                 (string-equal major-mode "xah-clojure-mode")
                 (string-equal major-mode "scheme-mode"))))
            (setq $p1 (point) $p2 (point))
            (insert *left-bracket *right-bracket)
            (search-backward *right-bracket )))
         (t (progn
              ;; wrap around “word”. basically, want all alphanumeric, plus hyphen and underscore, but don't want space or punctuations. Also want chinese chars
              ;; 我有一帘幽梦,不知与谁能共。多少秘密在其中,欲诉无人能懂。
              (skip-chars-backward "-_[:alnum:]")
              (setq $p1 (point))
              (skip-chars-forward "-_[:alnum:]")
              (setq $p2 (point))
              (goto-char $p2)
              (insert *right-bracket)
              (goto-char $p1)
              (insert *left-bracket)
              (goto-char (+ $p2 (length *left-bracket))))))))))

Now we define the commands:

(defun xah-insert-paren ()
  (xah-insert-bracket-pair "(" ")") )

(defun xah-insert-bracket ()
  (xah-insert-bracket-pair "[" "]") )

(defun xah-insert-brace ()
  (xah-insert-bracket-pair "{" "}") )

Here's more for Unicode Brackets (very useful if you write a lot structured text.)

(defun xah-insert-double-curly-quote“” () (interactive) (xah-insert-bracket-pair "“" "”") )
(defun xah-insert-curly-single-quote‘’ () (interactive) (xah-insert-bracket-pair "‘" "’") )
(defun xah-insert-single-angle-quote‹› () (interactive) (xah-insert-bracket-pair "‹" "›") )
(defun xah-insert-double-angle-quote«» () (interactive) (xah-insert-bracket-pair "«" "»") )
(defun xah-insert-ascii-double-quote () (interactive) (xah-insert-bracket-pair "\"" "\"") )
(defun xah-insert-ascii-single-quote () (interactive) (xah-insert-bracket-pair "'" "'") )
(defun xah-insert-emacs-quote () (interactive) (xah-insert-bracket-pair "`" "'") )
(defun xah-insert-corner-bracket「」 () (interactive) (xah-insert-bracket-pair "「" "」") )
(defun xah-insert-white-corner-bracket『』 () (interactive) (xah-insert-bracket-pair "『" "』") )
(defun xah-insert-angle-bracket〈〉 () (interactive) (xah-insert-bracket-pair "〈" "〉") )
(defun xah-insert-double-angle-bracket《》 () (interactive) (xah-insert-bracket-pair "《" "》") )
(defun xah-insert-white-lenticular-bracket〖〗 () (interactive) (xah-insert-bracket-pair "〖" "〗") )
(defun xah-insert-black-lenticular-bracket【】 () (interactive) (xah-insert-bracket-pair "【" "】") )
(defun xah-insert-tortoise-shell-bracket〔〕 () (interactive) (xah-insert-bracket-pair "〔" "〕") )

〔►see Unicode: Brackets, Quotes «»「」【】《》

Setting Up Keys

Now you can set any key to call your commands.

(global-set-key (kbd "<f8> 7") 'xah-insert-brace) ; {}
(global-set-key (kbd "<f8> 8") 'xah-insert-paren) ; ()
(global-set-key (kbd "<f8> 9") 'xah-insert-bracket) ; []


(global-set-key (kbd "M-7") 'xah-insert-brace) ; {}
(global-set-key (kbd "M-8") 'xah-insert-paren) ; ()
(global-set-key (kbd "M-9") 'xah-insert-bracket) ; []

〔►see Emacs: How to Define Keys

Moving Around Brackets

Emacs: Move Cursor to Bracket/Quote

Working with Brackets Topic

  1. Emacs: Highlight Brackets ({[]})
  2. Emacs: Insert Brackets by Pair
  3. Emacs: Delete Bracket ({[]}) by Pair
  4. Emacs: Select Line, between Quotes, Extend Selection
  5. Emacs: Move Cursor to Bracket/Quote
Like it? Buy Xah Emacs Tutorial. Thanks.