31

In my .emacs i have the following function that transposes a line

(defun move-line (n)
   "Move the current line up or down by N lines."
   (interactive "p")
   (let ((col (current-column))
         start
         end)
     (beginning-of-line)
     (setq start (point))
     (end-of-line)
     (forward-char)
     (setq end (point))
     (let ((line-text (delete-and-extract-region start end)))
       (forward-line n)
       (insert line-text)
       ;; restore point to original column in moved line
       (forward-line -1)
       (forward-char col))))

And I bind a key to it like this

(global-set-key (kbd "M-<down>") 'move-line)
;; this is the same as M-x global-set-key <return>

However, I want to bind M-up to move-line (-1) But I cant seem to be able to do it correctly:

;; M-- M-1 M-x global-set-key <return>

How do I define the above using global-set-key to call move-line -1?

yrral
  • 949
  • 1
  • 7
  • 11

5 Answers5

50

Not minutes after asking the question I figured it out by copy+pasting code. However I have no clue how it works.

(global-set-key (kbd "M-<up>") (lambda () (interactive) (move-line -1)))
yrral
  • 949
  • 1
  • 7
  • 11
  • 8
    You create a anomynous function (`lambda`) which is only a wrapper for your function called with the argument `-1`. Search the internet or emacs info for `lambda` if you want to get more information. – danielpoe Jun 23 '09 at 07:17
  • 1
    I like this style of setting key bindings for wrapper functions. Saves littering the namespace and I find it reads better. I suppose you could create a handy macro too: `(cmd (move-line -1))`. – d11wtq May 17 '13 at 12:31
  • 9
    Also important information in addition to what danielpoe said: the `(interactive)` is needed in any lisp function that's meant to "interact" with the user either via keybindings or other methods. That's what allows it to be keybound. If you don't have `(interactive)` in your function, it can _only_ be called from other code. –  Dec 10 '16 at 20:47
9

global-set-key only takes 2 arguments: the key sequence and the command you want to bind to it. So

(global-set-key (kbd "M-<down>") 'move-line)

works fine. But if you want to use move-line with an argument you need to wrap it in an anonymous (aka lamba) function so that it presents itself to global-set-key as one value.

Martin Redmond
  • 13,366
  • 6
  • 36
  • 32
3

You can simply ask for the number of lines you want and convert the input string into an integer:

(global-set-key (kbd "M-<up>") 
  (lambda () 
    (interactive) 
    (move-line (string-to-int (read-string "Lines: ")))))
2

I found this when I had the same problem, but I ended up solving it in another way.

(global-set-key (kbd "M-<down>") 'move-line)

(global-set-key (kbd "M-<up>") (kbd "C-u -1 M-<down>"))

Definitely not a perfect solution, since M-<down> could be reassigned and C-u -1 might not make sense on it, but since it's just my local init file, it should be no problem.

Also this obvious only works well for keyboard commands that you want to have reversed.

Nisse
  • 4,517
  • 1
  • 12
  • 7
0

You might want to check out the "transpose-lines" built-in function.

  • 1
    The Question and Answer can be generalized to How to set a key to a function with an argument, not this specific transpose lines example. There's always more than one way to do it ala Unix and/or Emacs – bsd Oct 29 '22 at 10:20