namin
namin

Reputation: 38189

Swap parentheses and square brackets in Emacs Paredit

How can I define a command in paredit mode that swaps parentheses and square brackets?

Upvotes: 6

Views: 2995

Answers (5)

Micah Elliott
Micah Elliott

Reputation: 10264

Here is a general version that can use whichever paredit-like cousin you like, to cycle between parens, square, and curly braces -- from anywhere inside the sexp. It uses the light-weight puni here, but smartparens and paredit both have their equivalent *-wrap-* and splice functions.

(defun cycle-pairs ()
  "Toggle parens, braces, brackets."
  (interactive)
  (save-excursion
    (when (not (string-match-p (regexp-quote (char-to-string (char-after))) "([{"))
      (backward-up-list)
      (when (eq ?\" (char-after)) ; up again if inside string
        (backward-up-list)))
    (progn
      (cl-case (char-after)
        ;; These can be substituted
        (?\( (puni-wrap-square))
        (?\[ (puni-wrap-curly))
        (?\{ (puni-wrap-round))
        ;; (?\< "(")
        )
      (forward-char)
      (puni-splice)))) ; also substituted

This can be extended to handle brokets, quotes, etc. There is a big advantage to rolling your own for this because several other solutions out there depend on substantial package dependencies that you may not want to pull into your Emacs.

Upvotes: 0

Jisang Yoo
Jisang Yoo

Reputation: 3745

So the task is to turn this, for example:

(blah
 (a (b)
    c))

into this:

(blah
 [a (b)
    c])

With paredit mode, move to start of the expression (a ..) and then:

C-M-SPC [ <right> M-s

Without paredit, but still wanting to maintain balanced parens during transitions, move to a and then press C-M-SPC multiple times until error and then (assuming that CUA mode is on):

C-x <timeout> <right> <backspace> <backspace> [ ] <left> C-v

Well that is complex, so let's stick with paredit mode version, and try to make a command out of it. Keyboard Macro Editor tells you the names of commands being used, so you would be able to come up with at least the following code:

(defun my-switch-to-square ()
  "Change (..) to [..]."
  (interactive)
  (mark-sexp --)
  (paredit-open-square --)
  (right-char --)
  (paredit-splice-sexp --))

-- indicates part of code we have not yet decided. After you read documentation of each function in the code, you learn what arguments to pass, and that there is no need to call mark-sexp. After rewriting docstring and adding a call to left-char, the code you end up with would be:

(defun my-switch-to-square ()
  "Change |(..) to |[..]. | is point position."
  (interactive)
  (paredit-open-square 1)
  (right-char 1)
  (paredit-splice-sexp)
  (left-char 1))

Upvotes: 11

Trey Jackson
Trey Jackson

Reputation: 74430

The following code does what you request. You can bind swap-parens to whatever key binding you want.

(defvar swap-paren-pairs '("()" "[]"))
(defun swap-parens-at-points (b e)
  (let ((open-char (buffer-substring b (+ b 1)))
        (paren-pair-list (append swap-paren-pairs swap-paren-pairs)))
    (while paren-pair-list
      (if (eq (aref open-char 0) (aref (car paren-pair-list) 0))
          (save-excursion
            (setq to-replace (cadr paren-pair-list))
            (goto-char b)
            (delete-char 1)
            (insert (aref to-replace 0))
            (goto-char (- e 1))
            (delete-char 1)
            (insert (aref to-replace 1))
            (setq paren-pair-list nil))
        (setq paren-pair-list (cdr paren-pair-list))))))

(defun swap-parens ()
  (interactive)
  (cond ((looking-at "\\s(")
         (swap-parens-at-points (point) (save-excursion (forward-sexp) (point))))
        ((and (> (point) 1) (save-excursion (forward-char -1) (looking-at "\\s)")))
         (swap-parens-at-points (save-excursion (forward-sexp -1) (point)) (point)))
        ((message "Not at a paren"))))

Upvotes: 6

event_jr
event_jr

Reputation: 17707

I'm not sure what you mean by "define command"? You can do it like this:

|(foo bar)

"|" is point.

;; Keyboard Macro Editor.  Press C-c C-c to finish; press C-x k RET to cancel.
;; Original keys: C-u [ C-f <M-up> C-b

Command: last-kbd-macro
Key: none

Macro:

C-u [       ;; paredit-open-square
C-f         ;; forward-char
<M-up>      ;; paredit-splice-sexp-killing-backward
C-b         ;; backward-char

Although to be honest, this type of usage scenario is more common for VIM trickery. I've never actually used this IRL.

Upvotes: 2

Andreas R&#246;hler
Andreas R&#246;hler

Reputation: 4804

bzr branch lp:s-x-emacs-werkstatt

will deliver a library inspired by paredit, but delivering more detailed commands

M-x ar-bracket-parentized-atpt RET

puts brackets around parenteses at point

look inside thing-at-point-utils.el and the other files what else is provided

Upvotes: 0

Related Questions