102

What is the easiest way to move selected region or line (if there is no selection) up or down in emacs? I'm looking for the same functionality as is in eclipse (bounded to M-up, M-down).

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
fikovnik
  • 3,473
  • 3
  • 28
  • 29
  • 2
    I'm pretty sure the idiom is to just kill what you want to move, use the usual navigation functions (including isearch, etc.) to move the point where you want the code to be, and then yank. There is a stack of kills, remember, so you don't even have to find the location immediately; you can gather up other bits to yank later. Personally, as a heavy Emacs user, I can't imagine any situation where I'd rather manually move a single line up or down. It just wouldn't be useful. – jrockway Mar 12 '10 at 02:27
  • 10
    @jrockway: thanks for your comment. I think everyone has his own way of doing things. I work a lot with eclipse and I'm very used to moving lines/regions. One of the great thing of having emacs very extensible is that this functionality can be easily added. – fikovnik Mar 14 '10 at 15:18
  • 4
    agreed. I frequently want to move lines up and down as you describe, whether or not I'm in an editor that supports it. Or, for that matter, whether or not I'm working on code. Word supports this (by paragraph) with Alt+Shift+Up and Down, and I map that in my editors whenever I can. – harpo Sep 28 '10 at 00:47
  • Yes, everyone is different, and contexts are different. I will add an anecdote, however. Many moon ago, before I came to Emacs, I too was in the same habit. I used a (good) editor that had keys for this, and it was just the way I was used to. Immediately after moving to Emacs, I wrote myself some code such as that mentioned on this page, to do the same thing: move lines or the region text up/down. Eventually, I stopped using this "feature" and just did what most people do in Emacs: kill + yank. I never looked back. Just one person's story, FWIW. – Drew Aug 21 '11 at 20:18
  • 10
    @Drew and jrockway : I think settle for less is hardly the emacs way. Moving lines is just quicker when you want to move them than kill/yankk is. And org-mode ofcourse has it implemented too for a reason: indeed there are many cases you want to move a line, moving a wrong placed statement in / out a block scope is one, alter statement order, alter document order (see org-mode), .. A lot of people use it in eclipse. What you don't use , you often don't miss, but the usefullness of moving lines is a fact for a whole lot of programmers – Peter Apr 06 '13 at 12:27
  • 3
    This is useful when editing git-rebase files, containing lists of commits that you may want to reorder. – Ken Williams Jul 17 '13 at 14:32

9 Answers9

57

Update: Install the move-text package from Marmalade or MELPA to get the following code.

Here's what I use, which works on both regions and individual lines:

(defun move-text-internal (arg)
  (cond
   ((and mark-active transient-mark-mode)
    (if (> (point) (mark))
        (exchange-point-and-mark))
    (let ((column (current-column))
          (text (delete-and-extract-region (point) (mark))))
      (forward-line arg)
      (move-to-column column t)
      (set-mark (point))
      (insert text)
      (exchange-point-and-mark)
      (setq deactivate-mark nil)))
   (t
    (let ((column (current-column)))
      (beginning-of-line)
      (when (or (> arg 0) (not (bobp)))
        (forward-line)
        (when (or (< arg 0) (not (eobp)))
          (transpose-lines arg)
          (when (and (eval-when-compile
                       '(and (>= emacs-major-version 24)
                             (>= emacs-minor-version 3)))
                     (< arg 0))
            (forward-line -1)))
        (forward-line -1))
      (move-to-column column t)))))

(defun move-text-down (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines down."
  (interactive "*p")
  (move-text-internal arg))

(defun move-text-up (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines up."
  (interactive "*p")
  (move-text-internal (- arg)))


(global-set-key [M-S-up] 'move-text-up)
(global-set-key [M-S-down] 'move-text-down)
sanityinc
  • 15,002
  • 2
  • 49
  • 43
  • 1
    Ive added this to EmacsWiki for you, it's here: http://www.emacswiki.org/emacs/MoveText – ocodo Oct 04 '10 at 22:48
  • Thanks! I think that code was already on there (http://www.emacswiki.org/emacs/basic-edit-toolkit.el) but this new page will be easier for people to find. – sanityinc Oct 05 '10 at 08:59
  • Ahh, cool I've not seen that before, looks like basic-edit-toolkit could use a cleanup. – ocodo Oct 05 '10 at 21:42
  • Just wanted to note here that I packaged `move-text.el` for use with `package.el` and uploaded it to marmalade-repo.org for easy installation. – sanityinc Jan 15 '12 at 09:45
  • I just spent half an hour adding `move-defun-up` and `move-defun-down` to this, and then I realized that as I was fixing bugs I kept `C-M-h C-w C-M-e`ing to get back to a good state. I didn't even *realize* that I knew how to do that, but I did realize that I don't need `move-defun-up`. – quodlibetor Apr 25 '13 at 21:39
  • @quodlibetor And of course, you can just select the defun with `C-M-h` and then move it up/down linewise with the `move-text-up` and `move-text-down` functions above. – sanityinc Apr 26 '13 at 15:50
  • Two questions about this: (1) How could I extend this function to expand the region to span whole lines when partial lines are selected (just like in Eclipse)? (2) Can I install this via el-get? – Lenar Hoyt Mar 13 '14 at 02:38
  • 1
    @mcb Yes, any ELPA package can be installed via el-get. (Note that even the el-get author is moving towards package.el these days.) To select full lines, I would suggest writing a separate function which quickly selects either the current line, or extends the current region to include full lines. Then just invoke that function before the move-text functions. – sanityinc Mar 13 '14 at 10:45
  • @sanityinc What is the advantage of package.el over el-get? – Lenar Hoyt Apr 02 '14 at 13:30
  • @mcb It's an official standard, built into every recent Emacs, and developers are actively designing their code for it. In many cases these days, `el-get` just delegates to `package.el`. Any post-install setup code that `el-get` might support can be handled with packages like `use-package`. – sanityinc Apr 03 '14 at 14:39
  • With `evil-mode`, this doesn't work properly because the pointer does not move with the text for some reason. If I am in `insert` mode, however, the pointer does update and work properly. Note this only happens when moving regions - with a single line, everything works just fine. Perhaps someone can propose a fix? – modulitos Jul 08 '15 at 08:23
  • 1
    @sanityinc Thank you for this. I just had a mini Emacs 'O' the first time I used it. Good stuff! – JS. Nov 10 '15 at 18:53
  • I have tried this and it moves only a line, but not the cursor, so pressing move twice basically swaps lines back again instead of moving the line up/down two positions. Is it supposed to work this way? – ars Jun 07 '16 at 15:02
  • This does not work in python files for me, doing `M-up` and `M-down` i see this in the minibuffer: `Can't splice top level.`. Does any one know why this happens and how to correct it? –  Jun 17 '16 at 00:53
  • @amirteymuri Then your `M-up` must be bound to a different command in that mode. Press `C-h k M-up` to find out what it is bound to there. – sanityinc Jun 17 '16 at 23:26
  • I now notice that it actually doesn't work in any mode, not only in python! What exactly is the process of getting it? I put your code in init.el, nothing happens. I go through the instructions of the homepage puting move-text.el in my load-path /usr/local/share/emacs/site-lisp and put the lines `(require 'move-text)` and `(move-text-default-bindings)` in init.el, still nothing happens. –  Jun 18 '16 at 20:28
  • I also made a directory `elisp` in my home (i have ubuntu) and put the move-text.el in it and it still didn't work. Do you have any idea what the problem could be? –  Jun 18 '16 at 20:30
  • @amirteymuri You will have to load the file too, and set up the keybindings. There are instructions in move-text.el for doing so. Hard to help you from here, sorry, but it sounds like your question is just related to general set-up of new elisp libraries. If you have a recent Emacs, try using M-x package-install to install move-text, then M-x move-text-default-keybindings. – sanityinc Jun 18 '16 at 23:06
54

A line can be moved using transpose-lines bound to C-x C-t. I don't know about regions, though.

I found this elisp snippet that does what you want, except you need to change the bindings.

(defun move-text-internal (arg)
   (cond
    ((and mark-active transient-mark-mode)
     (if (> (point) (mark))
            (exchange-point-and-mark))
     (let ((column (current-column))
              (text (delete-and-extract-region (point) (mark))))
       (forward-line arg)
       (move-to-column column t)
       (set-mark (point))
       (insert text)
       (exchange-point-and-mark)
       (setq deactivate-mark nil)))
    (t
     (beginning-of-line)
     (when (or (> arg 0) (not (bobp)))
       (forward-line)
       (when (or (< arg 0) (not (eobp)))
            (transpose-lines arg))
       (forward-line -1)))))

(defun move-text-down (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines down."
   (interactive "*p")
   (move-text-internal arg))

(defun move-text-up (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines up."
   (interactive "*p")
   (move-text-internal (- arg)))

(global-set-key [\M-\S-up] 'move-text-up)
(global-set-key [\M-\S-down] 'move-text-down)
peterh
  • 11,875
  • 18
  • 85
  • 108
Martin Wickman
  • 19,662
  • 12
  • 82
  • 106
  • There's a little problem with both this solution, and the almost identical one posted by @sanityinc: if I use the defined shortcut to move a region up and/or down, then immediately use the shortcut for undo (C-_ by default), the region simply disappears, and the message "Undo in region!" is displayed in the echo area. Another undo command yields the message "No further undo information for region". However, if I move the cursor, then command undo, the undo starts working again. A relatively minor irritation, but all other Emacs commands can be undone immediately without resorting to a trick. – Teemu Leisti Jul 15 '13 at 13:06
  • 3
    This is exactly the right way to do it. Default transpose line in Emacs is NOT correct functionality since the cursor moves to next line after the transpose making it difficult to repeat it step by step. – mythicalcoder Mar 21 '17 at 06:03
42

You should try drag-stuff !

It works exactly like eclipse Alt+Up/Down for single lines, as well as for selected region lines!

In addition to that it allows you to move words with Alt+Left/Right
This is exactly what you're looking for! And it is even available from the ELPA repos!

Other solutions never worked for me. Some of them were buggy(transposing lines while changing their order, wtf?) and some of them were moving exactly selected region, leaving unselected parts of the lines on their positions. But drag-stuff works exactly like in eclipse!

And even more! You can try selecting a region and using Alt+Left/Right ! This will transpose selected region by one character to the left or right. Amazing!

To enable it globally simply run this:

(drag-stuff-global-mode)
Aleks-Daniel Jakimenko-A.
  • 10,335
  • 3
  • 41
  • 39
14

I have written a couple of interactive functions for moving lines up/down:

;; move line up
(defun move-line-up ()
  (interactive)
  (transpose-lines 1)
  (previous-line 2))

(global-set-key [(control shift up)] 'move-line-up)

;; move line down
(defun move-line-down ()
  (interactive)
  (next-line 1)
  (transpose-lines 1)
  (previous-line 1))

(global-set-key [(control shift down)] 'move-line-down)

The keybindings are IntelliJ IDEA style, but you can use anything you want. I should probably implement some functions that operate on regions as well.

Bozhidar Batsov
  • 55,802
  • 13
  • 100
  • 117
6

Here is my snippet to move the current line or the lines spanned by the active region. It respects cursor position and highlighted region. And it won't break lines when the region doesn't begin/end at line border(s). (It is inspired by eclipse; I found the eclipse way more convenient than 'transpose-lines'.)

;; move the line(s) spanned by the active region up/down (line transposing)
;; {{{
(defun move-lines (n)
  (let ((beg) (end) (keep))
    (if mark-active 
        (save-excursion
          (setq keep t)
          (setq beg (region-beginning)
                end (region-end))
          (goto-char beg)
          (setq beg (line-beginning-position))
          (goto-char end)
          (setq end (line-beginning-position 2)))
      (setq beg (line-beginning-position)
            end (line-beginning-position 2)))
    (let ((offset (if (and (mark t) 
                           (and (>= (mark t) beg)
                                (< (mark t) end)))
                      (- (point) (mark t))))
          (rewind (- end (point))))
      (goto-char (if (< n 0) beg end))
      (forward-line n)
      (insert (delete-and-extract-region beg end))
      (backward-char rewind)
      (if offset (set-mark (- (point) offset))))
    (if keep
        (setq mark-active t
              deactivate-mark nil))))

(defun move-lines-up (n)
  "move the line(s) spanned by the active region up by N lines."
  (interactive "*p")
  (move-lines (- (or n 1))))

(defun move-lines-down (n)
  "move the line(s) spanned by the active region down by N lines."
  (interactive "*p")
  (move-lines (or n 1)))
Ji Han
  • 651
  • 8
  • 6
4

There is an entry in the emacs wiki just for this:

http://www.emacswiki.org/emacs/MoveLine

For moving regions:

http://www.emacswiki.org/emacs/MoveRegion

Manuel
  • 41
  • 1
1

There's no built-in. You can use transpose-lines (C-x C-t) but you cannot use it repeatedly. Look at the functions on http://www.schuerig.de/michael/blog/index.php/2009/01/16/line-movement-for-emacs/.

It should be easy to adapt that to regions, too.

Florian Thiel
  • 497
  • 4
  • 10
  • 1
    Btw, if you do decide to use the linked snippet, you might want to change the let statement to (let ((col (current-column)) (line-move-visual nil)), otherwise you get funny behavior with wrapped lines. – Leo Alekseyev Mar 11 '10 at 14:47
1

The transpose-paragraph function could help you.

You might also want to have a look to the transpose section in the Emacs manual. Essentially:

C-t
Transpose two characters (transpose-chars).
M-t
Transpose two words (transpose-words).
C-M-t
Transpose two balanced expressions (transpose-sexps).
C-x C-t
Transpose two lines (transpose-lines).
Roberto Aloi
  • 30,570
  • 21
  • 75
  • 112
0

I use the smart-shift package (in Melpa) for this. By default it rebinds C-C <arrow> to move a line or region. It moves horizontally by a major-mode-specific amount (e.g. c-basic-offset or python-indent-offset). Works on regions also.

;; binds C-C <arrows>
(when (require 'smart-shift nil 'noerror)
  (global-smart-shift-mode 1))
jpkotta
  • 9,237
  • 3
  • 29
  • 34