11

electric-pair-mode is a built-in mode for automatically inserting matching pairs of delimiters (parentheses, square brackets, etc.) based on the current major mode.

I know that I can define additional pairs (which will be global) like this:

(push '(?\' . ?\') electric-pair-pairs)      ; Automatically pair single-quotes
(push '(?\' . ?\') electric-pair-text-pairs) ; ... in comments

My question is, how can I define mode-specific pairs (e.g., //, == for org-mode)?

7

The correct way: fill a bug report through the proper channel of your project, e.g. org-submit-bug-report or report-emacs-bug and argue why the syntax class of your favourite character should be changed.

Alternatively, you could modify the proper syntax-table, (info "(elisp) Syntax Tables"), in your init.el.

Let's try Org:

(with-eval-after-load 'org
  (modify-syntax-entry ?/ "(/" org-mode-syntax-table)
  (modify-syntax-entry ?= "(=" org-mode-syntax-table)
  (add-hook 'org-mode-hook 'electric-pair-mode))

Alternatively, you can use the fallback variables. Here's a defun that should work, but that you might want to make prettier:

(defun rasmus/electric-pairs-with-local-pairs (pairs)
  "Start electric pair with buffer-local PAIRS.

  PAIRS is a list of strings and/or cons of strings."
  (require 'elec-pair)
  (let ((ec-lists '(electric-pair-pairs electric-pair-text-pairs)))
    (mapc 'make-local-variable ec-lists)        
    (mapc (lambda (L)
            (mapc (lambda (elm) (add-to-list L elm))
                  (mapcar (lambda (x)
                            (if (consp x)
                                (cons (string-to-char (car x))
                                      (string-to-char (cdr x)))
                              (cons (string-to-char x)
                                    (string-to-char x))))
                          pairs)))
          ec-lists))
  (electric-pair-mode t))

(with-eval-after-load 'org
  (add-hook 'org-mode-hook
            (lambda ()
              (rasmus/electric-pairs-with-local-pairs
               '("/" "=" ("`" . "'"))))))
  • Thanks for posting an answer that shows how to modify the syntax table. Out of the two suggestions in your answer, this is the approach I would prefer. W/r/t using the fallback variables, I came up with another solution that's a bit shorter than the defun in your answer. – itsjeyd Oct 24 '14 at 16:04
6

An easy way to do this is by making electric-pair-pairs and electric-pair-text-pairs buffer-local and customizing them in hooks for relevant modes.

Working example for org-mode:

(defvar org-electric-pairs '((?/ . ?/) (?= . ?=)) "Electric pairs for org-mode.")

(defun org-add-electric-pairs ()
  (setq-local electric-pair-pairs (append electric-pair-pairs org-electric-pairs))
  (setq-local electric-pair-text-pairs electric-pair-pairs))

(add-hook 'org-mode-hook 'org-add-electric-pairs)

Note that this approach is generalizable to other situations in which you want to modify the value of a global variable for specific modes.


Additional info: setq-local

From the Creating and Deleting Buffer-Local Bindings section of the Elisp manual:

Macro: setq-local variable value

This macro creates a buffer-local binding in the current buffer for VARIABLE, and gives it the buffer-local value VALUE. It is equivalent to calling make-local-variable followed by setq. VARIABLE should be an unquoted symbol.

2

This answer doesn't answer your question on how to configure electric-pair-mode. But it might lead you to the results you want.

The wrap-region package available on Melpa might be the answer to your problem. Here is its brief description from its github:

Wrap Region is a minor mode for Emacs that wraps a region with punctuations. For "tagged" markup modes, such as HTML and XML, it wraps with tags.

Here is how I have set it to work in my selected modes. The snippet also covers the points you raised in your question; about org-mode font property markers.

(require 'wrap-region)

;; Enable wrap-region in the following major modes
(dolist (hook '(emacs-lisp-mode-hook
                org-mode-hook))
  (add-hook hook 'wrap-region-mode))

(wrap-region-add-wrapper "`" "'") ; select region, hit ` then region -> `region'

(wrap-region-add-wrapper "=" "=" nil 'org-mode) ; select region, hit = then region -> =region= in org-mode
(wrap-region-add-wrapper "*" "*" nil 'org-mode) ; select region, hit * then region -> *region* in org-mode
(wrap-region-add-wrapper "/" "/" nil 'org-mode) ; select region, hit / then region -> /region/ in org-mode
(wrap-region-add-wrapper "_" "_" nil 'org-mode) ; select region, hit _ then region -> _region_ in org-mode
(wrap-region-add-wrapper "+" "+" nil 'org-mode))) ; select region, hit + then region -> +region+ in org-mode

I'd like to add that this package works really well with the expand-region package (also available on Melpa).

With these 2 packages, when I am in org-mode, doing: MY-EXPAND-REGION-BINDING * on a word will make it bold.

  • Thanks for your answer. I was aware of the wrap-region package; it's quite useful. I am currently trying to reduce the number of third-party packages I depend on, so I won't be using this solution, but it definitely deserves a mention here! :) – itsjeyd Oct 24 '14 at 15:52
1

Building on itsjeyd's answer:

(defmacro spw/add-mode-pairs (hook pairs)
  `(add-hook ,hook
             (lambda ()
               (setq-local electric-pair-pairs (append electric-pair-pairs ,pairs))
               (setq-local electric-pair-text-pairs electric-pair-pairs))))

(spw/add-mode-pairs 'emacs-lisp-mode-hook '((?` . ?')))
  • I had to escape characters in order to make it work (?\= . ?\=) – phoxd Nov 22 at 21:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.