How do I duplicate a whole line in Emacs?

2019-01-12 14:54发布

问题:

I saw this same question for VIM and it has been something that I myself wanted to know how to do for Emacs. In ReSharper I use CTRL-D for this action. What is the least number of commands to perform this in Emacs?

回答1:

I use

C-a C-SPACE C-n M-w C-y

which breaks down to

  • C-a: move cursor to start of line
  • C-SPACE: begin a selection ("set mark")
  • C-n: move cursor to next line
  • M-w: copy region
  • C-y: paste ("yank")

The aforementioned

C-a C-k C-k C-y C-y

amounts to the same thing (TMTOWTDI)

  • C-a: move cursor to start of line
  • C-k: cut ("kill") the line
  • C-k: cut the newline
  • C-y: paste ("yank") (we're back at square one)
  • C-y: paste again (now we've got two copies of the line)

These are both embarrassingly verbose compared to C-d in your editor, but in Emacs there's always a customization. C-d is bound to delete-char by default, so how about C-c C-d? Just add the following to your .emacs:

(global-set-key "\C-c\C-d" "\C-a\C- \C-n\M-w\C-y")

(@Nathan's elisp version is probably preferable, because it won't break if any of the key bindings are changed.)

Beware: some Emacs modes may reclaim C-c C-d to do something else.



回答2:

In addition to the previous answers you can also define your own function to duplicate a line. For example, putting the following in your .emacs file will make C-d duplicate the current line.

(defun duplicate-line()
  (interactive)
  (move-beginning-of-line 1)
  (kill-line)
  (yank)
  (open-line 1)
  (next-line 1)
  (yank)
)
(global-set-key (kbd "C-d") 'duplicate-line)


回答3:

Place cursor on line, if not at beginning do a CTRL-A, then:

CTRL-K

CTRL-K

CTRL-Y

CTRL-Y



回答4:

My version of a function to duplicate a line that works nice with undo and doesn't mess with the cursor position. It was the result of a discussion in gnu.emacs.sources from November 1997.

(defun duplicate-line (arg)
  "Duplicate current line, leaving point in lower line."
  (interactive "*p")

  ;; save the point for undo
  (setq buffer-undo-list (cons (point) buffer-undo-list))

  ;; local variables for start and end of line
  (let ((bol (save-excursion (beginning-of-line) (point)))
        eol)
    (save-excursion

      ;; don't use forward-line for this, because you would have
      ;; to check whether you are at the end of the buffer
      (end-of-line)
      (setq eol (point))

      ;; store the line and disable the recording of undo information
      (let ((line (buffer-substring bol eol))
            (buffer-undo-list t)
            (count arg))
        ;; insert the line arg times
        (while (> count 0)
          (newline)         ;; because there is no newline in 'line'
          (insert line)
          (setq count (1- count)))
        )

      ;; create the undo information
      (setq buffer-undo-list (cons (cons eol (point)) buffer-undo-list)))
    ) ; end-of-let

  ;; put the point in the lowest line and return
  (next-line arg))

Then you can define CTRL-D to call this function:

(global-set-key (kbd "C-d") 'duplicate-line)


回答5:

Instead of kill-line (C-k) as in C-a C-k C-k C-y C-y use the kill-whole-line command:

C-S-Backspace
C-y
C-y

The advantages over C-k include that it does not matter where point is on the line (unlike C-k which requires being at start of the line) and it also kills the newline (again something C-k does not do).



回答6:

Here's yet another function for doing this. My version doesn't touch the kill ring, and the cursor ends up on the new line where it was on the original. It will duplicate the region if it's active (transient mark mode), or default to duplicating the line otherwise. It will also make multiple copies if given a prefix arg, and comment out the original line if given a negative prefix arg (this is useful for testing a different version of a command/statement while keeping the old one).

(defun duplicate-line-or-region (&optional n)
  "Duplicate current line, or region if active.
With argument N, make N copies.
With negative N, comment out original line and use the absolute value."
  (interactive "*p")
  (let ((use-region (use-region-p)))
    (save-excursion
      (let ((text (if use-region        ;Get region if active, otherwise line
                      (buffer-substring (region-beginning) (region-end))
                    (prog1 (thing-at-point 'line)
                      (end-of-line)
                      (if (< 0 (forward-line 1)) ;Go to beginning of next line, or make a new one
                          (newline))))))
        (dotimes (i (abs (or n 1)))     ;Insert N times, or once if not specified
          (insert text))))
    (if use-region nil                  ;Only if we're working with a line (not a region)
      (let ((pos (- (point) (line-beginning-position)))) ;Save column
        (if (> 0 n)                             ;Comment out original with negative arg
            (comment-region (line-beginning-position) (line-end-position)))
        (forward-line 1)
        (forward-char pos)))))

I have it bound to C-c d:

(global-set-key [?\C-c ?d] 'duplicate-line-or-region)

This should never be re-assigned by a mode or anything because C-c followed by a single (unmodified) letter is reserved for user bindings.



回答7:

Nathan's addition to your .emacs file is the way to go but it could be simplified slightly by replacing

  (open-line 1)
  (next-line 1)

with

  (newline)

yielding

(defun duplicate-line()
  (interactive)
  (move-beginning-of-line 1)
  (kill-line)
  (yank)
  (newline)
  (yank)
)
(global-set-key (kbd "C-d") 'duplicate-line)


回答8:

I don't quite remember how line duplication works anywhere else, but as a former SciTE user I liked one thing about SciTE-way: it doesn't touch the cursor position! So all the recipies above weren't good enough for me, here's my hippie-version:

(defun duplicate-line ()
    "Clone line at cursor, leaving the latter intact."
    (interactive)
    (save-excursion
        (let ((kill-read-only-ok t) deactivate-mark)
            (toggle-read-only 1)
            (kill-whole-line)
            (toggle-read-only 0)
            (yank))))

Note that nothing gets actually killed in process, leaving marks and current selection intact.

BTW, why you guys so fond of jerking cursor around when there's this nice'n'clean kill-whole-line thingy (C-S-backspace)?



回答9:

install duplicate-thing from melpa

and add this keybinding:

(global-set-key (kbd "M-c") 'duplicate-thing)



回答10:

because i don't know, i'll start this round of golf with a slowball:

ctrl-k, y, y



回答11:

I have copy-from-above-command bound to a key and use that. It's provided with XEmacs, but I don't know about GNU Emacs.

`copy-from-above-command' is an interactive compiled Lisp function
-- loaded from "/usr/share/xemacs/21.4.15/lisp/misc.elc" (copy-from-above-command &optional ARG)

Documentation: 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.



回答12:

C-a C-k C-k C-y C-y


回答13:

something you might want to have in your .emacs is

(setq kill-whole-line t)

Which basically kills the entire line plus the newline whenever you invoke kill-line (i.e. via C-k). Then without extra code, you can just do C-a C-k C-y C-y to duplicate the line. It breaks down to

C-a go to beginning of line
C-k kill-line (i.e. cut the line into clipboard)
C-y yank (i.e. paste); the first time you get the killed line back; 
    second time gives the duplicated line.

But if you use this often then maybe a dedicated key binding might be a better idea, but the advantage of just using C-a C-k C-y C-y is you can duplicate the line elsewhere, instead of just below the current line.



回答14:

The defaults are horrible for this. However, you can extend Emacs to work like SlickEdit and TextMate, that is, copy/cut the current line when no text is selected:

(transient-mark-mode t)
(defadvice kill-ring-save (before slick-copy activate compile)
  "When called interactively with no active region, copy a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (message "Copied line")
     (list (line-beginning-position)
           (line-beginning-position 2)))))
(defadvice kill-region (before slick-cut activate compile)
  "When called interactively with no active region, kill a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (list (line-beginning-position)
           (line-beginning-position 2)))))

Place the above in .emacs. Then, to copy a line, M-w. To delete a line, C-w. To duplicate a line, C-a M-w C-y C-y C-y ....



回答15:

' I wrote my own version of duplicate-line, cause I don't want to screw up the killing ring.

  (defun jr-duplicate-line ()
    "EASY"
    (interactive)
    (save-excursion
      (let ((line-text (buffer-substring-no-properties
                        (line-beginning-position)
                        (line-end-position))))
        (move-end-of-line 1)
        (newline)
        (insert line-text))))
  (global-set-key "\C-cd" 'jr-duplicate-line)


回答16:

With recent emacs, you can use M-w anywhere in the line to copy it. So it becomes:

M-w C-a RET C-y


回答17:

I saw very complex solutions, anyway...

(defun duplicate-line ()
  "Duplicate current line"
  (interactive)
  (kill-whole-line)
  (yank)
  (yank))
(global-set-key (kbd "C-x M-d") 'duplicate-line)


回答18:

There is package called Avy It has command avy-copy-line. When you use that command, every line in your window gets letter combination. Then you just have to type combination and you get that line. This also works for region. Then you just have to type two combination.

Here you can see interface:

http://i68.tinypic.com/24fk5eu.png



回答19:

@[Kevin Conner]: Pretty close, so far as I know. The only other thing to consider is turning on kill-whole-line to include the newline in the C-k.



回答20:

I liked FraGGod's version, except for two things: (1) It doesn't check whether the buffer is already read-only with (interactive "*"), and (2) it fails on the last line of the buffer if that last line is empty (as you cannot kill the line in that case), leaving your buffer read-only.

I made the following changes to resolve that:

(defun duplicate-line ()
  "Clone line at cursor, leaving the latter intact."
  (interactive "*")
  (save-excursion
    ;; The last line of the buffer cannot be killed
    ;; if it is empty. Instead, simply add a new line.
    (if (and (eobp) (bolp))
        (newline)
      ;; Otherwise kill the whole line, and yank it back.
      (let ((kill-read-only-ok t)
            deactivate-mark)
        (toggle-read-only 1)
        (kill-whole-line)
        (toggle-read-only 0)
        (yank)))))


回答21:

When called interactively with no active region, COPY (M-w) a single line instead :

(defadvice kill-ring-save (before slick-copy activate compile)
  "When called interactively with no active region, COPY a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (message "Copied line")
     (list (line-beginning-position)
           (line-beginning-position 2)))))

When called interactively with no active region, KILL (C-w) a single line instead.

(defadvice kill-region (before slick-cut activate compile)
  "When called interactively with no active region, KILL a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (message "Killed line")
     (list (line-beginning-position)
           (line-beginning-position 2)))))

Also, on a related note:

(defun move-line-up ()
  "Move up the current line."
  (interactive)
  (transpose-lines 1)
  (forward-line -2)
  (indent-according-to-mode))

(defun move-line-down ()
  "Move down the current line."
  (interactive)
  (forward-line 1)
  (transpose-lines 1)
  (forward-line -1)
  (indent-according-to-mode))

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


回答22:

ctrl-k, ctrl-k, (position to new location) ctrl-y

Add a ctrl-a if you're not starting at the beginning of the line. And the 2nd ctrl-k is to grab the newline character. It can be removed if you just want the text.



回答23:

I write one for my preference.

(defun duplicate-line ()
  "Duplicate current line."
  (interactive)
  (let ((text (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
        (cur-col (current-column)))
    (end-of-line) (insert "\n" text)
    (beginning-of-line) (right-char cur-col)))
(global-set-key (kbd "C-c d l") 'duplicate-line)

But I found this would have some problem when current line contains multi-byte characters (e.g. CJK characters). If you encounter this issue, try this instead:

(defun duplicate-line ()
  "Duplicate current line."
  (interactive)
  (let* ((text (buffer-substring-no-properties (point-at-bol) (point-at-eol)))
         (cur-col (length (buffer-substring-no-properties (point-at-bol) (point)))))
    (end-of-line) (insert "\n" text)
    (beginning-of-line) (right-char cur-col)))
(global-set-key (kbd "C-c d l") 'duplicate-line)


回答24:

With prefix arguments, and what is (I hope) intuitive behaviour:

(defun duplicate-line (&optional arg)
  "Duplicate it. With prefix ARG, duplicate ARG times."
  (interactive "p")
  (next-line 
   (save-excursion 
     (let ((beg (line-beginning-position))
           (end (line-end-position)))
       (copy-region-as-kill beg end)
       (dotimes (num arg arg)
         (end-of-line) (newline)
         (yank))))))

The cursor will remain on the last line. Alternatively, you might want to specify a prefix to duplicate the next few lines at once:

(defun duplicate-line (&optional arg)
  "Duplicate it. With prefix ARG, duplicate ARG times."
  (interactive "p")
  (save-excursion 
    (let ((beg (line-beginning-position))
          (end 
           (progn (forward-line (1- arg)) (line-end-position))))
      (copy-region-as-kill beg end)
      (end-of-line) (newline)
      (yank)))
  (next-line arg))

I find myself using both often, using a wrapper function to switch the behavior of the prefix argument.

And a keybinding: (global-set-key (kbd "C-S-d") 'duplicate-line)



回答25:

;; http://www.emacswiki.org/emacs/WholeLineOrRegion#toc2
;; cut, copy, yank
(defadvice kill-ring-save (around slick-copy activate)
  "When called interactively with no active region, copy a single line instead."
  (if (or (use-region-p) (not (called-interactively-p)))
      ad-do-it
    (kill-new (buffer-substring (line-beginning-position)
                                (line-beginning-position 2))
              nil '(yank-line))
    (message "Copied line")))
(defadvice kill-region (around slick-copy activate)
  "When called interactively with no active region, kill a single line instead."
  (if (or (use-region-p) (not (called-interactively-p)))
      ad-do-it
    (kill-new (filter-buffer-substring (line-beginning-position)
                                       (line-beginning-position 2) t)
              nil '(yank-line))))
(defun yank-line (string)
  "Insert STRING above the current line."
  (beginning-of-line)
  (unless (= (elt string (1- (length string))) ?\n)
    (save-excursion (insert "\n")))
  (insert string))

(global-set-key (kbd "<f2>") 'kill-region)    ; cut.
(global-set-key (kbd "<f3>") 'kill-ring-save) ; copy.
(global-set-key (kbd "<f4>") 'yank)           ; paste.

add the elisp above to you init.el, and you get cut/copy whole line function now, then you can F3 F4 to duplicate a line.



回答26:

The simplest way is Chris Conway's method.

C-a C-SPACE C-n M-w C-y

That's the default way mandated by EMACS. In my opinion, it's better to use the standard. I am always careful towards customization one's own key-binding in EMACS. EMACS is already powerful enough, I think we should try our best to adapt to its own key-bindings.

Though it's a bit lengthy, but when you are used to it, you can do fast and will find this is fun!



回答27:

This functionality should match up with JetBrains' implementation in terms of duplicating both by line or region, and then leaving the point and/ or active region as expected:

Just a wrapper to around the interactive form:

(defun wrx/duplicate-line-or-region (beg end)
  "Implements functionality of JetBrains' `Command-d' shortcut for `duplicate-line'.
   BEG & END correspond point & mark, smaller first
   `use-region-p' explained: 
   http://emacs.stackexchange.com/questions/12334/elisp-for-applying-command-to-only-the-selected-region#answer-12335"
  (interactive "r")
  (if (use-region-p)
      (wrx/duplicate-region-in-buffer beg end)
    (wrx/duplicate-line-in-buffer)))

Which calls this,

(defun wrx/duplicate-region-in-buffer (beg end)
  "copy and duplicate context of current active region
   |------------------------+----------------------------|
   |        before          |           after            |
   |------------------------+----------------------------|
   | first <MARK>line here  | first line here            |
   | second item<POINT> now | second item<MARK>line here |
   |                        | second item<POINT> now     |
   |------------------------+----------------------------|
   TODO: Acts funky when point < mark"
  (set-mark-command nil)
  (insert (buffer-substring beg end))
  (setq deactivate-mark nil))

Or this

(defun wrx/duplicate-line-in-buffer ()
  "Duplicate current line, maintaining column position.
   |--------------------------+--------------------------|
   |          before          |          after           |
   |--------------------------+--------------------------|
   | lorem ipsum<POINT> dolor | lorem ipsum dolor        |
   |                          | lorem ipsum<POINT> dolor |
   |--------------------------+--------------------------|
   TODO: Save history for `Cmd-Z'
   Context: 
   http://stackoverflow.com/questions/88399/how-do-i-duplicate-a-whole-line-in-emacs#answer-551053"
  (setq columns-over (current-column))
  (save-excursion
    (kill-whole-line)
    (yank)
    (yank))
  (let (v)
    (dotimes (n columns-over v)
      (right-char)
      (setq v (cons n v))))
  (next-line))

And then I have this bound to meta+shift+d

(global-set-key (kbd "M-D") 'wrx/duplicate-line-or-region)


回答28:

Here's a function for duplicating current line. With prefix arguments, it will duplicate the line multiple times. E.g., C-3 C-S-o will duplicate the current line three times. Doesn't change kill ring.

(defun duplicate-lines (arg)
  (interactive "P")
  (let* ((arg (if arg arg 1))
         (beg (save-excursion (beginning-of-line) (point)))
         (end (save-excursion (end-of-line) (point)))
         (line (buffer-substring-no-properties beg end)))
    (save-excursion
      (end-of-line)
      (open-line arg)
      (setq num 0)
      (while (< num arg)
        (setq num (1+ num))
        (forward-line 1)
        (insert-string line))
      )))

(global-set-key (kbd "C-S-o") 'duplicate-lines)


回答29:

I cannot believe all these complicated solutions. This is two keystrokes:

<C-S-backspace> runs the command kill-whole-line

C-/ runs the command undo

So <C-S-backspace> C-/ to "copy" a whole line (kill and undo).

You can, of course, combine this with numeric and negative args to kill multiple lines either forward or backward.



回答30:

This feels more natural, with respect to the selected answer by Chris Conway.

(global-set-key "\C-c\C-d" "\C-a\C- \C-n\M-w\C-y\C-p\C-e")

This allows you to duplicate a line multiple times by simply repeating the \C-c\C-d key strokes.