【问题标题】:Move line/region up and down in emacs在emacs中上下移动行/区域
【发布时间】:2010-03-11 09:31:02
【问题描述】:

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

【问题讨论】:

  • 我很确定这个习惯用法就是杀死你想要移动的东西,使用通常的导航功能(包括 isearch 等)移动你想要代码所在的点,然后然后猛拉。记住,有一堆杀戮,所以你甚至不必立即找到位置;您可以收集其他位以便稍后拉取。就个人而言,作为一个重度 Emacs 用户,我无法想象在任何情况下我宁愿手动向上或向下移动一行。它只是没有用。
  • @jrockway:感谢您的评论。我认为每个人都有自己的做事方式。我经常使用 Eclipse,而且我非常习惯于移动线条/区域。让 emacs 非常可扩展的一大好处是可以轻松添加此功能。
  • 同意。我经常想按照您的描述上下移动线条,无论我是否在支持它的编辑器中。或者,就此而言,无论我是否正在编写代码。 Word 使用 Alt+Shift+Up 和 Down 支持这一点(按段落),我会尽可能在我的编辑器中映射它。
  • @Drew 和 jrockway :我认为满足于更少并不是 emacs 的方式。当你想移动它们时,移动线条比 kill/yankk 更快。当然 org-mode 也实现了它是有原因的:确实有很多情况你想移动一行,将错误放置的语句移入/移出块范围是一个,改变语句顺序,改变文档顺序(参见 org-模式),..很多人在eclipse中使用它。不使用的东西,你经常不会错过,但是移动线的用处是很多程序员的事实
  • 这在编辑 git-rebase 文件时很有用,其中包含您可能想要重新排序的提交列表。

标签: emacs


【解决方案1】:

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

这是我使用的,适用于区域和单行:

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

【讨论】:

  • 我已经为你添加了这个到 EmacsWiki,它在这里:emacswiki.org/emacs/MoveText
  • 谢谢!我认为该代码已经存在(emacswiki.org/emacs/basic-edit-toolkit.el),但这个新页面将更容易被人们找到。
  • 啊,很酷,我以前没见过,看起来 basic-edit-toolkit 可以使用清理。
  • @mcb 是的,任何 ELPA 包都可以通过 el-get 安装。 (请注意,即使是 el-get 作者最近也在转向 package.el。)要选择完整行,我建议编写一个单独的函数来快速选择当前行,或扩展当前区域以包含完整行。然后在移动文本函数之前调用该函数。
  • @sanityinc 谢谢你。第一次使用时,我刚刚拥有一个迷你 Emacs 'O'。好东西!
【解决方案2】:

可以使用绑定到C-x C-ttranspose-lines 移动一行。不过,我不知道地区。

我发现 thiselisp sn-p 可以满足您的需求,但您需要更改绑定。

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

【讨论】:

  • 感谢 sn-p。这正是我想要的!
  • 这个解决方案和@sanityinc 发布的几乎相同的解决方案都有一个小问题:如果我使用定义的快捷方式向上和/或向下移动区域,则立即使用快捷方式撤消(默认为 C-_),该区域会消失,并显示消息“在区域中撤消!”显示在回波区。另一个撤消命令产生消息“区域没有进一步的撤消信息”。但是,如果我移动光标,然后命令撤消,撤消将再次开始工作。相对较小的刺激,但所有其他 Emacs 命令都可以立即撤消,而无需借助任何技巧。
  • 这正是正确的做法。 Emacs 中的默认转置行不是正确的功能,因为转置后光标会移动到下一行,因此很难逐步重复。
【解决方案3】:

你应该试试drag-stuff

对于单行以及选定区域行,它的工作方式与 eclipse Alt+Up/Down 完全相同!

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

其他解决方案从未对我有用。其中一些是错误的(在改变它们的顺序时转置线,wtf?),其中一些正在移动精确选择的区域,在它们的位置上留下未选择的部分。但是drag-stuff 的工作方式与 eclipse 中的完全一样!

还有更多!您可以尝试选择一个区域并使用 Alt+Left/Right !这会将所选区域向左或向右移动一个字符。太棒了!

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

(drag-stuff-global-mode)

【讨论】:

  • 它确实需要左右移动可选,因为它覆盖了默认的 emacs 键盘映射。
  • @Slomojo 也许吧!你为什么不在emacswiki上建议呢? :)
  • 我以前用这个,现在用smart-shift。我喜欢它用于水平移动的 DWIM。
  • 在键绑定开始工作之前,在 init 文件中需要 (drag-stuff-define-keys)。这在这个 github 中有解释:github.com/rejeep/drag-stuff.el
【解决方案4】:

我已经编写了几个用于向上/向下移动线条的交互函数:

;; 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】:

    这是我的 sn-p 移动当前行或活动区域跨越的行。它尊重光标位置和突出显示的区域。当区域不在线边界开始/结束时,它不会断线。 (它的灵感来自 eclipse;我发现 eclipse 方式比 'transpose-lines' 更方便。)

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

    【讨论】:

    • 对我来说,移动文本包坏了。您的解决方案在我的系统上完美运行。谢谢!
    【解决方案6】:

    在 emacs wiki 中有一个条目就是为了这个:

    http://www.emacswiki.org/emacs/MoveLine

    对于移动区域:

    http://www.emacswiki.org/emacs/MoveRegion

    【讨论】:

      【解决方案7】:

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

      它也应该很容易适应地区。

      【讨论】:

      • 顺便说一句,如果您决定使用链接的 sn-p,您可能希望将 let 语句更改为 (let ((col (current-column)) (line-move-visual nil) ),否则你会得到有趣的换行行为。
      【解决方案8】:

      transpose-paragraph 函数可以帮助您。

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

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

      【讨论】:

        【解决方案9】:

        为此,我使用了 smart-shift 包(在 Melpa 中)。默认情况下,它重新绑定C-C &lt;arrow&gt; 以移动一条线或区域。它水平移动一个特定于主要模式的量(例如 c-basic-offset 或 python-indent-offset)。也适用于区域。

        ;; binds C-C <arrows>
        (when (require 'smart-shift nil 'noerror)
          (global-smart-shift-mode 1))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-13
          • 1970-01-01
          • 2012-02-28
          • 1970-01-01
          相关资源
          最近更新 更多