If there is something you find yourself doing over and over again in emacs, you can create a shortcut. For example, something I find myself doing repeatedly is duplicating a line and making a modification on the second line.
int v1 = 1; int v2 = 2;
The way I used to do this is:
- I moved to the beginning of the line
- I set the marker
- I moved to the end of the line
- I copied the marked region
- I pressed enter
- and finally I yanked the copied line
There may be a quicker way, even in vanilla emacs. However, this doesn’t take long, but it is still better to have it available with a single keypress.
(defun duplicate-current-line () (interactive) (beginning-of-line nil) (let ((b (point))) (end-of-line nil) (copy-region-as-kill b (point))) (beginning-of-line 2) (open-line 1) (yank) (back-to-indentation)) (global-set-key "\C-cd" 'duplicate-current-line)
This does have a few minor problems – e.g. it doesn’t work if we are on the last line of a buffer. Fixing this is left as an exercise for the reader.
Seems like a good place to use emacs’ keyboard macros.
Doing it in a vanilla emacs is easier when setting
(setq kill-whole-line t) ; C-k deletes the end of line
I find this setting useful in almost all circumstances.
The keystrokes for duplicating a line are then C-a; C-k, C-y; C-y. Very quick as you can keep holding C and the last two are the same key.
Here is my version, it works on the last line in the buffer, and even if this last line does not end with newline. It also keeps the cursor position within the line:
(defun duplicate-current-line () (interactive)
(let ((str (concat
(buffer-substring (point)
(save-excursion (end-of-line) (point)))
“\n”
(buffer-substring (save-excursion (beginning-of-line) (point))
(point)))))
(insert str)
))
I don’t kill-whole-line but I do it like thomas11: C-a C-k C-k C-y C-y, or sometimes C-a C-k C-y C-j C-y. Save the M-w (copy) stuff for multi-line blocks.
Hello,
Take a look at misc.el’s (part of Emacs) copy-from-above-command:
“Copy characters from previous nonblank line, starting just above point.
Copy ARG characters, but not past the end of that line.
If no argument given, copy the entire rest of the line.
The characters copied are inserted in the buffer before point.”
Hi folks, thanks for the responses.
@maziran – I know about keyboard macros and use them quite extensively if you’re talking about the ones using C-x ( … C-x ). I think this solves a different problem though: making multiple similar edits within one session rather than a few similar edits across multiple different sessions. That is unless there is a way to store keyboard macros between invocations of emacs. Even then, I don’t see the benefit over a short interactive defun such as I described in the post.
@thomas11 & Dan Lewis – I don’t like setting kill-whole-line, but that is a neat trick with yanking twice, thanks. However, I think I still like having the shortcut as C-c d as it is even quicker and I use this so frequently the 0.2s saving adds up. I’ve also added back-to-indentation to duplicate-current-line which ties in well with my normal usage.
@Dima – thanks for the fix. I’ll have to take a look at it later to see how it works. I had previously imagined a fix using (newline).
@Peter BARABAS – thanks for the pointer to copy-from-above-command. It looks more robust than my version and should probably be added in place of copy-region-as-kill. There are a lot of useful functions in the .el files that aren’t loaded in at start-up so I should probably take a look at these at some point.
I have a similar command I bound to M-o which I use a lot. It is special because it also increments numbers, which is perfect for your example. When there are no numbers, it acts like other copy lines. It’s pretty old, so there is probably a better way to do it. It is:
;;; Original author: ttn at netcom dot com, 28-Jan-1996
;;; Modified for multiple lines: Eric
(defun another-line (num-lines)
“Copies line, preserving cursor column, and increments any numbers found.
This should probably be generalized in the future.
Argument NUM-LINES is the number of lines to modify.”
(interactive “p”)
(if (not num-lines) (setq num-lines 0) (setq num-lines (1- num-lines)))
(let* ((col (current-column))
(bol (save-excursion (forward-line (- num-lines)) (beginning-of-line) (point)))
(eol (progn (end-of-line) (point)))
(line (buffer-substring bol eol)))
(goto-char bol)
(while (re-search-forward “[0-9]+” eol 1)
(let ((num (string-to-int (buffer-substring
(match-beginning 0) (match-end 0)))))
(replace-match (int-to-string (1+ num))))
(setq eol (save-excursion (goto-char eol) (end-of-line) (point))))
(goto-char bol)
(insert line “\n”)
(move-to-column col)))
Hi Eric,
Nice function, and good idea to include the increment – I like it. The less I need to do manually the better of course 🙂
I’ve used the code below for many years to do something similar. You kill whatever text you want to copy and call cbb-copy-kill-ring-regexp which prompts you for a regexp.
Then, you do cbb-yank-with-regexp, which yanks the copied text and prompts for replacement text.
;;;; copy-regexp.el
;;;; emacs lisp for kill/yank with regexp
(defvar cbb-copy-regexp-text nil
“Holds the text which will be used in the next yank-regexp operation”)
(defvar cbb-copy-regexp-regexp nil
“Holds the regexp which will be used in the next yank-regexp operation”)
(defun cbb-copy-kill-ring-regexp ()
“Copy the kill-ring-yank-pointer and prompt for source regexp”
(interactive “”)
(setq cbb-copy-regexp-text (car kill-ring-yank-pointer))
(setq cbb-copy-regexp-regexp (read-string “Replace regexp: “)))
(defun cbb-yank-with-regexp ()
“Yank cbb-copy-regexp-text at point and prompt for a replacement string”
(interactive “”)
(push-mark (point))
(let ((opoint (point)))
(insert cbb-copy-regexp-text)
(let ((repl (read-string
(concat “Replace regexp ” cbb-copy-regexp-regexp ” with: “))))
(save-excursion
(exchange-point-and-mark)
(while (re-search-forward cbb-copy-regexp-regexp (mark))
(replace-match repl))))))
Hi Chris,
I don’t think the intention of your code is similar to mine. Mine is simply to provide a shortcut to duplicate the current line. As far as I can see, yours doesn’t do that.
You may modify the default copy and cut commands so that they act on the current line when no text is selected. That’s how SlickEdit and TextMate do it. To implement it in Emacs, see http://www.emacswiki.org/emacs/SlickCopy