13

I've been using cursive's Structural Editing (a paredit-like mode for Intellij and Cursive) and I found a nice command there called move form-up (and down).

Move form up does this (the | is the point):

(+ 1 |(+ 2 3) 4) => (+ (+ 2 3) 1 4)

and Move form down is analogous:

(+ 1 |(+ 2 3) 4) => (+ 1 4 (+ 2 3))

Is it possible to do this on Emacs paredit-mode ? I think it is not on the basic paredit set (at least it is not described on this cheat sheet

2
  • Nice. Now that's an operation I could see myself doing.
    – PythonNut
    May 30, 2015 at 2:29
  • 3
    FWIW, at least for the two examples you show, plain C-M-t does about the same thing. But I guess what you mean is that the same form continues to be moved, rather than having swapping progress to the next pair of sexps.
    – Drew
    May 30, 2015 at 3:05

3 Answers 3

17

As @Drew suggested, C-M-t does what you want. The rule is like this:

  • Put point in the middle of two sexps.
  • Press C-M-t (which runs transpose-sexp).
  • The left sexp is always moved to the right along with point. This is very important, otherwise you will be confused to no end.

Or, you can simply think that this is not transposing but simply to move forward, you have to place point after the sexp you want to move.

However, there's one convenient that is, when you want to transpose the left sexp backward, you use the same C-M-t but supplied with a negative prefix argument and this is inconvenient. Another issue is that point won't follow the backward transposed left sexp. To solve this issue, we create a reverse version of transpose-sexps:

(defun reverse-transpose-sexps (arg)
  (interactive "*p")
  (transpose-sexps (- arg))
  ;; when transpose-sexps can no longer transpose, it throws an error and code
  ;; below this line won't be executed. So, we don't have to worry about side
  ;; effects of backward-sexp and forward-sexp.
  (backward-sexp (1+ arg))
  (forward-sexp 1))

Then, bind it to C-M-y which is next to C-M-t and is free in default Emacs.

(global-set-key (kbd "C-M-y") 'reverse-transpose-sexps)

Here is a demo; you will notice that (MY-SEXP1) is on the left all the time to be moved forward/backward:

enter image description here

0
5

It's possible to do it with lispy, which is an advanced form of paredit-mode.

For your use case:

  • w moves up
  • s moves down

The only thing is that the cursor has to be at paren, which it is in your example:

(+ 1 |(+ 2 3) 4)

You can also move several sexps at once with w and s, if you mark them with a region. See the documentation for more info.

1
  • ;; Is there a way to use only those two commands? ;; I seem to loose "M-left" and "M-right" when doing so:` (add-hook 'clojure-mode-hook (lambda () (lispy-mode 1) (local-set-key (kbd "M-<up>") 'lispy-move-up) (local-set-key (kbd "M-<down>") 'lispy-move-down) ))`
    – nha
    Oct 31, 2017 at 12:31
1

Here is another command (from library misc-cmds.el) that does pretty much what you request. Just bind it to a single key (C-M-t or whatever). Use it to move a sexp both forward and backward repeatedly.

A negative prefix arg transposes backward, like transpose-sexp, but it also leaves point ready to do another backward transposition. And when repeating, a negative prefix arg just flips the direction.

So if you bind the command to, say, C-o, then C-o C-o C-o C-- C-o C-o C-- C-o moves the sexp that is to the left of point to the right three times, then to the left twice, then to the right once.

A numeric prefix arg has an effect only for the first use, i.e., not when repeating - when repeating, the movement is always one sexp at a time.

(defun reversible-transpose-sexps (arg)
  "Reversible and repeatable `transpose-sexp'.
Like `transpose-sexps', but:
 1. Leaves point after the moved sexp.
 2. When repeated, a negative prefix arg flips the direction."
  (interactive "p")
  (when (eq last-command 'rev-transp-sexps-back) (setq arg  (- arg)))
  (transpose-sexps arg)
  (unless (natnump arg)
    (when (or (> emacs-major-version 24)
              (and (= emacs-major-version 24)  (> emacs-minor-version 3)))
      (backward-sexp (abs arg)) (skip-syntax-backward " ."))
    (setq this-command  'rev-transp-sexps-back)))

(Note: The behavior of transpose-sexps changed in 24.4, hence the version test here. Prior to 24.4, point ended up in the right place to continue (e.g. repeat). Starting with 24.4, point is in the wrong place for that. Whether that is a regression or adds a useful feature or fixes some other bug is presumably in the eye of the beholder. ;-) I filed Emacs bug #20698 for this, just now.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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