在emacs中上下移动行/区域


85

在emacs中向上或向下移动选定区域或线条(如果没有选择)的最简单方法是什么?我正在寻找与日食相同的功能(仅限于M-up,M-down)。


1
我敢肯定,习惯用法就是杀死您要移动的内容,使用常规的导航功能(包括isearch等)将您想要的代码移至所需位置,然后再进行猛拉。请记住,这里有很多人被杀,所以您甚至不必立即查找位置。您可以收集其他部分以供稍后提取。就个人而言,作为Emacs的沉重用户,我无法想象要手动向上或向下移动单行的任何情况。只是没有用。
jrockway

7
@jrockway:感谢您的评论。我认为每个人都有自己的处事方式。我在eclipse上工作很多,我非常习惯于移动线条/区域。具有非常可扩展的emacs的一大优点是可以轻松添加此功能。
fikovnik 2010年

3
同意 无论您是否在支持该功能的编辑器中,我通常都希望按照您的描述上下移动行。或者,就此而言,无论我是否在编写代码。Word通过Alt + Shift + Up和Down支持此功能(按段落),并且我会尽可能地将其映射到编辑器中。
harpo 2010年

6
@Drew和jrockway:我认为少花钱几乎不是emacs的方式。当您想移动线条时,移动线条比kill / yankk更快。当然,组织模式的实现也是有一个原因的:确实有很多情况您想移动一行,在块范围内移入/移出错误的放置语句是一种,更改语句顺序,更改文档顺序(请参见org-模式),..很多人在日食中使用它。你不要用什么,你往往不会错过,但动线的有用性是一大堆的程序员的一个事实
彼得

2
这在编辑git-rebase文件时非常有用,该文件包含您可能要重新排序的提交列表。
肯·威廉姆斯

Answers:


47

可以使用绑定到的转置线移动一条线C-x C-t。邓诺虽然地区。

我发现这个elisp片段可以满足您的需求,但您需要更改绑定。

(defun move-text-internal (arg)
   (cond
    ((and mark-active transient-mark-mode)
     (if (> (point) (mark))
            (exchange-point-and-mark))
     (let ((column (current-column))
              (text (delete-and-extract-region (point) (mark))))
       (forward-line arg)
       (move-to-column column t)
       (set-mark (point))
       (insert text)
       (exchange-point-and-mark)
       (setq deactivate-mark nil)))
    (t
     (beginning-of-line)
     (when (or (> arg 0) (not (bobp)))
       (forward-line)
       (when (or (< arg 0) (not (eobp)))
            (transpose-lines arg))
       (forward-line -1)))))

(defun move-text-down (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines down."
   (interactive "*p")
   (move-text-internal arg))

(defun move-text-up (arg)
   "Move region (transient-mark-mode active) or current line
  arg lines up."
   (interactive "*p")
   (move-text-internal (- arg)))

(global-set-key [\M-\S-up] 'move-text-up)
(global-set-key [\M-\S-down] 'move-text-down)

感谢您的摘要。这正是我想要的!
fikovnik 2010年

这个解决方案和@sanityinc发布的几乎相同的解决方案都存在一点问题:如果我使用定义的快捷方式上下移动区域,则立即使用该快捷方式进行撤消(默认情况下为C-_),该区域就会消失,并显示消息“撤消该区域!”。显示在回声区域中。另一个撤消命令产生消息“该区域没有其他撤消信息”。但是,如果我移动光标,然后执行撤消命令,撤消将再次开始工作。刺激程度相对较小,但是可以立即撤消所有其他Emacs命令,而无需采取任何技巧。
Teemu Leisti

1
这是正确的方法。Emacs中的默认转置行不是正确的功能,因为光标会在转置后移至下一行,从而很难一步一步地重复它。
mythicalcoder

51

更新:move-text从Marmalade或MELPA安装软件包以获取以下代码。

这是我使用的方法,可在区域和单独的行上使用:

(defun move-text-internal (arg)
  (cond
   ((and mark-active transient-mark-mode)
    (if (> (point) (mark))
        (exchange-point-and-mark))
    (let ((column (current-column))
          (text (delete-and-extract-region (point) (mark))))
      (forward-line arg)
      (move-to-column column t)
      (set-mark (point))
      (insert text)
      (exchange-point-and-mark)
      (setq deactivate-mark nil)))
   (t
    (let ((column (current-column)))
      (beginning-of-line)
      (when (or (> arg 0) (not (bobp)))
        (forward-line)
        (when (or (< arg 0) (not (eobp)))
          (transpose-lines arg)
          (when (and (eval-when-compile
                       '(and (>= emacs-major-version 24)
                             (>= emacs-minor-version 3)))
                     (< arg 0))
            (forward-line -1)))
        (forward-line -1))
      (move-to-column column t)))))

(defun move-text-down (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines down."
  (interactive "*p")
  (move-text-internal arg))

(defun move-text-up (arg)
  "Move region (transient-mark-mode active) or current line
  arg lines up."
  (interactive "*p")
  (move-text-internal (- arg)))


(global-set-key [M-S-up] 'move-text-up)
(global-set-key [M-S-down] 'move-text-down)

1
我已经为您添加到EmacsWiki,它在这里:emacswiki.org/emacs/MoveText
ocodo 2010年

谢谢!我认为该代码已经存在(emacswiki.org/emacs/basic-edit-toolkit.el),但是这个新页面将使人们更容易找到。
sanityinc

嗯,很酷,我之前从未见过,看来basic-edit-toolkit可以使用清理功能。
ocodo 2010年

1
@mcb是的,可以通过el-get安装任何ELPA软件包。(请注意,如今,即使是el-get的作家也正在朝package.el迈进。)要选择完整行,我建议编写一个单独的函数,该功能可以快速选择当前行,或扩展当前区域以包含完整行。然后只需在移动文本功能之前调用该功能。
sanityinc 2014年

1
@sanityinc谢谢你。第一次使用时,我只有一个迷你Emacs'O'。好东西!
JS。

31

你应该尝试drag-stuff

它与eclipse Alt+ Up/完全一样,适用Down于单行以及选定的区域线!

除此之外,它还允许您使用Alt+ Left/移动单词,Right
这正是您要寻找的!它甚至可以从ELPA仓库中获得

其他解决方案对我没用。其中一些是越野车(在更改顺序时换行,wtf?),而另一些则在精确选择的区域内移动,从而使未选择的部分保留在其位置上。但drag-stuff工作原理完全类似于日食!

甚至更多!您可以尝试选择一个地区并使用Alt+ Left/ Right!这会将所选区域的位置向左或向右移一个字符。惊人!

要全局启用它,只需运行以下命令:

(drag-stuff-global-mode)

实际上,它需要左右移动是可选的,因为它覆盖了默认的emacs键盘映射。
ocodo 2014年

@Slomojo也许!您为什么不在emacswiki上建议呢?:)
Aleks-Daniel Jakimenko-A。

1
我曾经用过,现在我用的是智能班。我喜欢DWIM进行水平移动。
jpkotta 2015年

1
(drag-stuff-define-keys)键绑定开始起作用之前,需要在init文件中进行操作。在此github中对此进行了解释:github.com/rejeep/drag-stuff.el
agent18

11

我编写了一些交互式函数来上下移动行:

;; move line up
(defun move-line-up ()
  (interactive)
  (transpose-lines 1)
  (previous-line 2))

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

;; move line down
(defun move-line-down ()
  (interactive)
  (next-line 1)
  (transpose-lines 1)
  (previous-line 1))

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

键绑定是IntelliJ IDEA样式,但是您可以使用任何所需的东西。我可能还应该实现一些在区域上运行的功能。


5

这是我的代码段,用于移动当前行或活动区域所跨越的行。它尊重光标位置和突出显示的区域。当区域不在线条边界处开始/结束时,它不会中断线条。(它受到日食的启发;我发现日食比“转置线”更方便。)

;; move the line(s) spanned by the active region up/down (line transposing)
;; {{{
(defun move-lines (n)
  (let ((beg) (end) (keep))
    (if mark-active 
        (save-excursion
          (setq keep t)
          (setq beg (region-beginning)
                end (region-end))
          (goto-char beg)
          (setq beg (line-beginning-position))
          (goto-char end)
          (setq end (line-beginning-position 2)))
      (setq beg (line-beginning-position)
            end (line-beginning-position 2)))
    (let ((offset (if (and (mark t) 
                           (and (>= (mark t) beg)
                                (< (mark t) end)))
                      (- (point) (mark t))))
          (rewind (- end (point))))
      (goto-char (if (< n 0) beg end))
      (forward-line n)
      (insert (delete-and-extract-region beg end))
      (backward-char rewind)
      (if offset (set-mark (- (point) offset))))
    (if keep
        (setq mark-active t
              deactivate-mark nil))))

(defun move-lines-up (n)
  "move the line(s) spanned by the active region up by N lines."
  (interactive "*p")
  (move-lines (- (or n 1))))

(defun move-lines-down (n)
  "move the line(s) spanned by the active region down by N lines."
  (interactive "*p")
  (move-lines (or n 1)))

1
对我来说,移动文本包坏了。您的解决方案可以在我的系统上完美运行。感谢那!
符文·卡加德


1

没有内置的。您可以使用转置线(Cx Ct),但不能重复使用。查看http://www.schuerig.de/michael/blog/index.php/2009/01/16/line-movement-for-emacs/上的功能。

也应该容易地使其适应区域。


1
顺便说一句,如果您决定使用链接的代码段,则可能需要将let语句更改为(let((col(current-column))(line-move-visual nil))),否则,换行会导致有趣的行为。
利奥·阿列克谢耶夫

1

transpose-paragraph功能可以为您提供帮助。

您可能还想看看Emacs手册中的转置部分。本质上:

C-t
Transpose two characters (transpose-chars).
M-t
Transpose two words (transpose-words).
C-M-t
Transpose two balanced expressions (transpose-sexps).
C-x C-t
Transpose two lines (transpose-lines).

0

我为此使用了智能换档套件(在Melpa中)。默认情况下,它将重新绑定C-C <arrow>以移动线或区域。它以特定于主要模式的水平移动(例如c-basic-offset或python-indent-offset)。也适用于区域。

;; binds C-C <arrows>
(when (require 'smart-shift nil 'noerror)
  (global-smart-shift-mode 1))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.