如何在Emacs中复制整行?

175

我看到了关于VIM的相同问题,这也是我自己在Emacs中想要知道如何实现的。在ReSharper中,我使用CTRL-D来执行此操作。在Emacs中执行此操作的最少命令数是多少?


2
当然,这是emacs,所以TMTOWTDI - 有22种!(还在不断增加中)http://c2.com/cgi/wiki?ThereIsMoreThanOneWayToDoIt - Tom
34个回答

160

我使用

C-a C-SPACE C-n M-w C-y
  • C-a:将光标移动到行首
  • C-SPACE:开始选择(“设置标记”)
  • C-n:将光标移动到下一行
  • M-w:复制区域
  • C-y:粘贴(“yank”)

上述内容

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

TMTOWTDI(There's More Than One Way To Do It)

  • C-a:将光标移动到行首
  • C-k:剪切(kill)整行
  • C-k:剪切换行符
  • C-y:粘贴(yank)(回到原来的状态)
  • C-y:再次粘贴(现在我们有两个副本了)

相比于编辑器中的C-d,这两种方法显得过于冗长,在Emacs中总是可以进行定制。默认情况下,C-d绑定到delete-char,那么我们使用C-c C-d怎么样?只需要在.emacs文件中添加以下内容:

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

(@Nathan的elisp版本可能更可取,因为如果任何键绑定被更改,它不会中断。)

注意:一些Emacs模式可能会重新使用C-c C-d来执行其他操作。


6
嗨!请注意,如果你设置了'(setq kill-whole-line t)',你只需要按下一个'C-k'(解决方案2),因为它已经将换行符与行内容一起删除。这是我首选使用的'C-k'方式。祝好, 丹尼尔 - danielpoe
202
这真的很尴尬。 - tofutim
23
C-S-backspace C-y C-y 是什么意思? - ericzma
1
M-w是什么?用哪个键? - Bala
4
“M”代表“元”(通常为Esc或Alt键,具体取决于你的键盘布局)。 “M-w”是同时按下“元”和“w” (在我的键盘上是“Alt-w”)。 - Chris Conway
显示剩余8条评论

104

除了前面的答案,你还可以定义自己的函数来复制一行。例如,将以下内容放入您的.emacs文件中将使C-d复制当前行。

(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)

我在使用这个程序时遇到了以下错误: 符号的函数定义无效:move-beginning-of-line - Rohaq
7
问题在于,“Del”键也被绑定用于复制该行…… - David Gomes
那么,有什么想法可以将“Del”从这个函数中解绑吗? - Alexander Shcheblikin
好的,找到了一个解决方案,可以将“Del”键恢复正常,同时保留新的“C-d”功能:在定义“C-d”之后需要添加(global-set-key (kbd "<delete>") 'delete-char) - Alexander Shcheblikin
1
在空行上尝试这样做会导致插入两行,而不是一行。我不知道为什么。有没有简单的解决方法? - Zelphir Kaltstahl

79

将光标放在行上,如果不在开头,请使用 CTRL-A 快捷键,然后:

CTRL-K

CTRL-K

CTRL-Y

CTRL-Y


我认为第二个C-Y是不必要的。 - Bastien Léonard
4
没有它,就不会有重复。 - epatel
21
使用C-S-Backspace(kill-whole-line)代替C-k。这样你就不必改变光标位置或删除换行符。 - Nietzche-jou
这个很好用,但是没有更简单的方法吗? - Stryker

55

这是我设计的一个函数,用于复制一行,可以与撤销功能完美配合,并且不会影响光标位置。它是1997年11月gnu.emacs.sources讨论的结果。

(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))

然后你可以定义CTRL-D来调用这个函数:

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

太棒了!撤销和光标位置功能使得这个程序成为最好的之一。谢谢! - ptrn
此外,链接中还有一些关于区域的代码! - pcarvalho
非常好的解决方案。谢谢。 - Plankalkül
非常好用。感谢您提供的解决方案。 - Stryker
2
@pesche crux-duplicate-current-line-or-region 对我来说更好,因为使用你的函数会撤销行复制和上一次操作。 - rofrol

54

使用kill-whole-line命令代替C-kkill-line),例如:C-a C-k C-k C-y C-y

C-S-Backspace
C-y
C-y
C-k的劣势在于必须在行首才能使用,而M-0 C-k则无论光标在哪一行都可以使用,并且它还会删除换行符(这点C-k做不到)。

2
恭喜 @RayVega!我尝试了这个解决方案,在我的GNU Emacs 23.3.1中运行得非常好。这个解决方案对有些人不起作用吗?这是你(自己)问题的最佳答案。 - JS.
1
你应该把这个答案接受为正确的。它完全做到了你所要求的,并且使用了“最少的命令”。 - Davor Cubranic
这可能是我最喜欢的方法,因为它需要最少的按键。 - Kirk Walla

34

这里是另一个执行此操作的函数。我的版本不会触及kill ring,并且光标最终停留在原始行上的新行上。如果区域处于活动状态(瞬时标记模式),它将复制该区域,否则默认复制该行。如果给定前缀参数,它还将创建多个副本,并在给定负前缀参数的情况下注释掉原始行(这对于测试命令/语句的不同版本同时保留旧版本非常有用)。

(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)))))

我已将它绑定到C-c d

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

这个变量不应该被模式或其他任何东西重新赋值,因为 C-c 加上一个单独的(未修改的)字母是保留给用户绑定的。


1
我把这个放在我的.emacs文件中,但是当我尝试使用C-c d时,我得到了错误信息“command-execute: Wrong type argument: commandp, duplicate-line-or-region”。有任何想法吗?我正在Windows上使用Emacs 25.1.1。 - junius
非常棒的解决方案,我很欣赏区域功能和带有负面参数的注释功能。同时也喜欢建议的按键绑定。 - Alex Trueman
@junius C-c d 是保留的键盘绑定用于 kill-whole-line,请使用其他键绑定。 - Kirk Walla

21
Nathan对于你的.emacs文件所做的添加是可行的,但可以通过替换

来稍微简化。
  (open-line 1)
  (next-line 1)

使用

  (newline)

易弯曲的

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

这很好。谢谢! - tejasbubane
(progn (beginning-of-line)(insert (thing-at-point 'line))) (progn (行首)(插入 (thing-at-point 'line))) - knobo

9

从Melpa安装duplicate-thing:

M-x package-install RET duplicate-thing

并将此键绑定添加到初始化文件中:

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


看起来这些日期不存在。 - MarkSkayff

5

我不太记得其他地方的行复制是如何工作的,但作为一名前SciTE用户,我喜欢SciTE的一件事:它不会影响光标位置!因此,上面所有的方法都不够好,这里是我的“嬉皮士”版本:

(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))))

请注意,进程中实际上没有任何东西被杀死,标记和当前选择保持不变。
顺便问一下,为什么你们喜欢在光标周围晃来晃去,而不使用这个漂亮干净的删除整行功能(C-S-backspace)?

5
我已将 copy-from-above-command 绑定到一个按键上,并使用它。这是 XEmacs 提供的,但我不知道 GNU Emacs 是否也有类似功能。

`copy-from-above-command` 是一个交互式编译过的 Lisp 函数
-- 加载自 "/usr/share/xemacs/21.4.15/lisp/misc.elc" (copy-from-above-command &optional ARG)

文档: 从上一行非空白字符复制字符,起点就在光标上方。复制 ARG 个字符,但不超过那一行的末尾。 如果没有给出参数,则复制整行剩余部分。被复制的字符将插入在光标之前的缓冲区中。


至于版本23,它也是标准GNU Emacs发行版的一部分。 - viam0Zah
似乎不在我的版本中。需要加载某些内容吗?我的版本是GNU Emacs 23.2.1 (amd64-portbld-freebsd8.1) of 2010-11-14 on [host clipped] - qmega
2
@qmega 你需要执行 (require 'misc)。 - Dmitry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接