ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Emacs 中复制整行?

我看到了 this same question for VIM,我自己也想知道如何为 Emacs 做这件事。在 ReSharper 中,我使用 CTRL-D 来执行此操作。在 Emacs 中执行此操作的最少命令数是多少?

当然,它是 emacs 所以 TMTOWTDI - 有 22 个! (计数)c2.com/cgi/wiki?ThereIsMoreThanOneWayToDoIt

C
Chris Conway

我用

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

分解为

Ca:将光标移动到行首

C-SPACE:开始选择(“设置标记”)

Cn:移动光标到下一行

Mw:复制区域

Cy:粘贴(“猛拉”)

之前所提

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

等同于同一件事 (TMTOWTDI)

Ca:将光标移动到行首

ck:剪切(“杀死”)行

ck:剪切换行符

Cy:粘贴(“yank”)(我们回到第一格)

Cy:再次粘贴(现在我们有两个副本)

与您的编辑器中的 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 来做其他事情。


你好!请注意,如果您有“(setq kill-whole-line t)”,则只需要一个“Ck”(解决方案2),因为它已经将换行符连同行的内容一起杀死了。我更喜欢使用“Ck”。干杯,丹尼尔
这真的很尴尬。
C-S-backspace C-y C-y 呢?
什么是兆瓦?使用哪个键?
@Bala“M”是“Meta”(通常是 Esc 或 Alt,这取决于您的键盘布局)。 “Mw”同时是“Meta”和“w”(在我的键盘上,“Alt-w”)。
N
Nate

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

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

我收到以下错误:Symbol's function definition is void: move-beginning-of-line
问题在于“Del”键也绑定到复制行......
那么,关于如何从这个函数中取消绑定 Del 的任何想法?
好的,找到了在保留新 C-d 的同时将 Del 恢复正常的解决方案:需要在 C-d 定义之后添加 (global-set-key (kbd "<delete>") 'delete-char)
在空行上尝试会导致插入两行,而不是仅插入一行。我不知道为什么。有简单的解决方法吗?
H
Hulk1991

将光标放在行上,如果不是在开始时执行 CTRL-A,则:

CTRL-K

CTRL-K

CTRL-Y

CTRL-Y


我认为不需要第二个 CY。
没有它就不会重复
使用 CS-Backspace (kill-whole-line) 而不是 Ck。您不必纠结光标位置或终止换行符。
这很好用,但没有捷径可以做到这一点吗?
p
pesche

我的复制行的函数版本与撤消配合得很好,并且不会弄乱光标位置。这是 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))

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

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

出色的!撤消和光标位置功能使其成为最好的。谢谢!
此外,在链接上也有一些地区代码!
非常好的解决方案。谢谢
效果很好。感谢您的解决方案。
@pesche crux-duplicate-current-line-or-region 对我来说效果更好,因为使用您的函数它还可以撤消行重复和最后一次操作。
R
Ray

使用 kill-whole-line 命令代替 C-a C-k C-k C-y C-y 中的 kill-line (C-k):

C-S-Backspace
C-y
C-y

优于 C-k 的优点包括点在行中的位置无关紧要(与 C-k 不同,它需要位于行首)并且它还会终止换行(同样,C-k 不会这样做)。


感谢@RayVega!我尝试了这个解决方案,它就像一个冠军(无论如何在我的 GNU Emacs 23.3.1 中)。这个解决方案对某些人不起作用吗?这是您(自己的)问题的最佳答案。
你应该接受这个答案是正确的。它完全按照您的要求进行,并且以“最少的命令数量”执行。
这可能是我最喜欢的方法,因为它需要最少的击键次数。
q
qmega

这是执行此操作的另一个功能。我的版本没有触及杀伤环,光标最终出现在原来的新行上。如果区域处于活动状态(瞬态标记模式),它将复制该区域,否则默认复制该行。如果给定前缀 arg,它还将制作多个副本,如果给定负前缀 arg,它将注释掉原始行(这对于测试不同版本的命令/语句同时保留旧版本很有用)。

(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 后跟一个(未修改的)字母是为用户绑定保留的。


我把它放在我的 .emacs 文件中,但是当我尝试使用 C-c d 时,我得到了错误 command-execute: Wrong type argument: commandp, duplicate-line-or-region。知道怎么了?我在 Windows 上使用 Emacs 25.1.1
非常好的解决方案,我很欣赏区域功能和带有否定参数的评论功能。也喜欢建议的键绑定。
@junius C-c dkill-whole-line 保留 kbd,使用另一个 kbd。
p
pw.

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)

这很好。谢谢!
(progn (beginning-of-line)(insert (thing-at-point 'line)))
e
extrav

从 melpa 安装重复的东西:

Mx 软件包安装 RET 重复的东西

并将此键绑定添加到 init file

(global-set-key (kbd "Mc") '重复的东西)


看起来到这个日期不在那里。
m
mk-fg

我不太记得行复制在其他任何地方是如何工作的,但作为前 SciTE 用户,我喜欢 SciTE-way 的一件事:它不会触及光标位置!所以上面所有的食谱对我来说都不够好,这是我的嬉皮版本:

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

请注意,在过程中实际上没有任何东西被杀死,留下标记和当前选择完好无损。

顺便说一句,当有这个漂亮的'n'clean kill-whole-line thingy(CS-backspace)时,为什么你们这么喜欢摇晃光标?


v
viam0Zah

我已将 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 发行版的一部分。
我的版本里好像没有。是否必须加载一些东西?我的版本是 GNU Emacs 23.2.1 (amd64-portbld-freebsd8.1) of 2010-11-14 on [host clipped]
@qmega你需要做(需要'misc)。
e
easeout

因为我不知道,我将用慢球开始这一轮高尔夫:

ctrl-k, y, y


p
polyglot

您可能希望在 .emacs 中拥有的东西是

(setq kill-whole-line t)

每当您调用kill-line(即通过Ck)时,它基本上会杀死整行加上换行符。然后无需额外代码,您只需执行 Ca Ck Cy Cy 即可复制该行。它分解为

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.

但是,如果您经常使用它,那么专用键绑定可能是一个更好的主意,但仅使用 Ca Ck Cy Cy 的优点是您可以在其他地方复制该行,而不是在当前行下方。


J
Joyer

' 我编写了自己的 duplicate-line 版本,因为我不想搞砸杀戮环。

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

G
Glorfindel

有一个名为 Avy 的包它有命令 avy-copy-line。当您使用该命令时,窗口中的每一行都会获得字母组合。然后你只需要输入组合,你就会得到那条线。这也适用于地区。然后你只需要输入两个组合。

在这里你可以看到界面:

https://i.stack.imgur.com/63mnV.png


s
sverrejoh
C-a C-k C-k C-y C-y

M
Marius Andersen

默认值是可怕的。但是,您可以扩展 Emacs 使其像 SlickEdit 和 TextMate 一样工作,即在未选择文本时复制/剪切当前行:

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

将上述内容放在 .emacs 中。然后,要复制一行,M-w。要删除一行,C-w。要复制一行,C-a M-w C-y C-y C-y ...


p
phils

我喜欢 FraGGod 的版本,除了两件事:(1)它不检查缓冲区是否已经用 (interactive "*") 只读,以及(2)如果缓冲区的最后一行为空,它会在缓冲区的最后一行失败(因为在这种情况下你不能杀死这条线),让你的缓冲区只读。

我进行了以下更改以解决该问题:

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

L
Louis Kottmann

使用最近的 emacs,您可以在行中的任何位置使用 Mw 来复制它。所以它变成:

M-w C-a RET C-y

真的吗?那会是哪个“最近的”Emacs? 24.4 的情况并非如此:你得到“现在没有设置标记,所以没有区域。”
当前的 Emacs 是 24.5,并且 M-w 绑定到 easy-kill。检查您执行 C-h c M-w 时得到的结果
在 Emacs 24.5.1 中不起作用。在插入前面的空白行后,仅从行首复制到同一行的开头。
s
songyuanyao

无论如何,我看到了非常复杂的解决方案......

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

请注意,这会弄乱杀死环。
当它是最后一行并且文件没有以新行结尾时,这会将行附加到自身上
y
yPhil

当在没有活动区域的情况下以交互方式调用时,COPY (Mw) 改为一行:

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

在没有活动区域的情况下以交互方式调用时,改为 KILL (Cw) 一行。

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

此外,在相关说明中:

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

(defun move-line-down ()
  "Move the current line down."
  (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)

我喜欢添加 move-line-up/down - 因为那将是我的下一个问题!
A
Allen

@[Kevin Conner]:据我所知,非常接近。唯一需要考虑的另一件事是打开 kill-whole-line 以在 Ck 中包含换行符。


@Allen:删除 @[Kevin Conner] 中的 []
H
Hulk1991

ctrl-k, ctrl-k, (定位到新位置) ctrl-y

如果您不是从行首开始,请添加 ctrl-a。第二个 ctrl-k 是抓取换行符。如果您只想要文本,可以将其删除。


这必须是这里最直接的方法。谢谢!
A
AesopHimself

此功能应与 JetBrains 的实现相匹配,即按行或区域复制,然后按预期保留点和/或活动区域:

只是围绕交互式表单的包装器:

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

这就是所谓的,

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

或这个

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

然后我把这个绑定到 meta+shift+d

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

D
Dan Garland

如果您使用 Spacemacs,您可以简单地使用 duplicate-line-or-region,绑定到:

SPC x l d 

Y
Y.H Wong

Melpa 上有一个名为“move-dup”的软件包可以帮助您。

免责声明:我是该软件包的作者。


p
prnsml

这是复制当前行的功能。使用前缀参数,它将多次复制该行。例如,C-3 C-S-o 会将当前行复制 3 次。不改变杀戮戒指。

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

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

k
kuanyui

我根据自己的喜好写一篇。

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

但是我发现当当前行包含多字节字符(例如 CJK 字符)时,这会有一些问题。如果您遇到此问题,请尝试以下操作:

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

A
Andy

我无法相信所有这些复杂的解决方案。这是两次击键:

<C-S-backspace> 运行命令 kill-whole-line

C-/ 运行命令 undo

所以 <C-S-backspace> C-/ 来“复制”一整行(杀死和撤消)。

当然,您可以将其与数字和负 args 结合起来,以向前或向后终止多行。


不幸的是,就像我的情况一样:“请注意,许多文本终端会阻止您键入键序列 CS-退格键。” gnu.org/software/emacs/manual/html_node/emacs/…
k
krsoni

正如其他答案中提到的,将击键绑定到 lisp 代码比将它们绑定到另一个击键更好。使用@mw 的答案,代码会复制该行并将标记移动到新行的末尾。此修改将标记位置保持在新行的同一列:

fun duplicate-line ()
  (interactive)
  (let ((col (current-column)))
    (move-beginning-of-line 1)
    (kill-line)
    (yank)
    (newline)
    (yank)
    (move-to-column col)))

K
Karthik

使用前缀参数,以及(我希望)什么是直观的行为:

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

光标将停留在最后一行。或者,您可能希望指定一个前缀来一次复制接下来的几行:

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

我发现自己经常使用这两种方法,使用包装函数来切换前缀参数的行为。

还有一个键绑定:(global-set-key (kbd "C-S-d") 'duplicate-line)