如何在Emacs的标记环中前进和后退

34
在Emacs中,C-u C-SPC 可以"跳转到标记,并从弹出本地标记环的位置设置标记"。是否有一种方法可以沿着标记环相反的方向移动?比如说,你已经使用了多次C-u C-SPC命令,现在想要回到之前看到过的某个标记,而不用绕全部的标记环。

3
在这种情况下,如果是一个众所周知或经常访问的位置,将标记保存在寄存器中可能会很有用。我不确定是否需要自己遍历标记环。 - Sid Heroor
1
虽然这不允许向后移动,但它有助于更快地浏览整个环,这在许多情况下可能已经是你所需要的全部:将变量set-mark-command-repeat-pop设置为非nil,这将允许你在按一次C-u C-SPC后仅需按C-SPC。来自手册 - quazgar
3
像往常一样,Helm 有相应的解决方法:helm-all-mark-rings 可以显示所有标记,并让您使用普通的 Helm 自动完成来浏览它们。您可以根据内容或行号进行自动完成! - sandos
8个回答

17
与之前的答案不同,这个仅执行被要求的操作:C-u C-SPC 的反向操作。我认为这是最有用的。
(defun unpop-to-mark-command ()
  "Unpop off mark ring. Does nothing if mark ring is empty."
  (interactive)
      (when mark-ring
        (setq mark-ring (cons (copy-marker (mark-marker)) mark-ring))
        (set-marker (mark-marker) (car (last mark-ring)) (current-buffer))
        (when (null (mark t)) (ding))
        (setq mark-ring (nbutlast mark-ring))
        (goto-char (marker-position (car (last mark-ring))))))

与scottfrazer的答案相比,该命令在光标和标记移动方式上有微妙的差异,更准确地反映了C-u C-spc,并且不要求前一个命令是unpop/pop-to-mark-command。请注意保留HTML标签。

scottfrazer在2010年的回答非常相似(并且回答了所提出的问题)。您可能想讨论一下为什么您的函数差异是对其进行改进。 - phils
1
当然。我已经编辑了我的回答。 - Ian Kelling
1
你有适用于global-mark-ring的版本吗?这样我们就可以在不同的缓冲区之间来回切换了。 - Len Trigg

12

这是我刚刚花了太多时间解决的问题。与其他解决方案的不同之处在于它适用于缓冲区,即它适用于“全局标记环”。我的目标是模拟类似于Eclipse或IntelliJ的历史浏览。我将其绑定到M-left和M-right,显然你可以为此选择不同的键。

(defun marker-is-point-p (marker)
  "test if marker is current point"
  (and (eq (marker-buffer marker) (current-buffer))
       (= (marker-position marker) (point))))

(defun push-mark-maybe () 
  "push mark onto `global-mark-ring' if mark head or tail is not current location"
  (if (not global-mark-ring) (error "global-mark-ring empty")
    (unless (or (marker-is-point-p (car global-mark-ring))
                (marker-is-point-p (car (reverse global-mark-ring))))
      (push-mark))))


(defun backward-global-mark () 
  "use `pop-global-mark', pushing current point if not on ring."
  (interactive)
  (push-mark-maybe)
  (when (marker-is-point-p (car global-mark-ring))
    (call-interactively 'pop-global-mark))
  (call-interactively 'pop-global-mark))

(defun forward-global-mark ()
  "hack `pop-global-mark' to go in reverse, pushing current point if not on ring."
  (interactive)
  (push-mark-maybe)
  (setq global-mark-ring (nreverse global-mark-ring))
  (when (marker-is-point-p (car global-mark-ring))
    (call-interactively 'pop-global-mark))
  (call-interactively 'pop-global-mark)
  (setq global-mark-ring (nreverse global-mark-ring)))

(global-set-key [M-left] (quote backward-global-mark))
(global-set-key [M-right] (quote forward-global-mark))

我刚刚发现了这个,结合 visible-mark-modC-u C-SPC。改变了生活。 - Josh.F
你是如何将它与 visible-mark-mod 结合起来的?@Josh.F - alper
我不确定它只是在标记历史记录上进行循环跳转 @spopejoy - alper

6

在回复scottfrazer的非常方便的解决方案之后,这里提供一些建议,与该解决方案配合使用,使得可以轻松地根据需要在标记环中反向移动,而无需为每个方向使用不同的键绑定。

我使用cua-selection-mode,因此对于我来说,C-SPC绑定到cua-set-mark,但我已经将其编写为宏,以便通知绑定到C-SPC的任何函数,并验证它可以与默认的set-mark-command一起工作。

要取消弹出,请只需提供负前缀参数。例如:C--C-SPC

cua-set-mark的一个好处是,在初始的C-uC-SPC之后,您可以继续使用C-SPC连续弹出标记,我在这里包含了这种行为:在初始的C--C-SPC之后,您可以继续使用C-SPC取消弹出。要再次反转方向并调用pop-to-mark,只需再次提供正参数,即C-uC-SPC

(defmacro my-unpop-to-mark-advice ()
  "Enable reversing direction with un/pop-to-mark."
  `(defadvice ,(key-binding (kbd "C-SPC")) (around my-unpop-to-mark activate)
     "Unpop-to-mark with negative arg"
     (let* ((arg (ad-get-arg 0))
            (num (prefix-numeric-value arg)))
       (cond
        ;; Enabled repeated un-pops with C-SPC
        ((eq last-command 'unpop-to-mark-command)
         (if (and arg (> num 0) (<= num 4))
             ad-do-it ;; C-u C-SPC reverses back to normal direction
           ;; Otherwise continue to un-pop
           (setq this-command 'unpop-to-mark-command)
           (unpop-to-mark-command)))
        ;; Negative argument un-pops: C-- C-SPC
        ((< num 0)
         (setq this-command 'unpop-to-mark-command)
         (unpop-to-mark-command))
        (t
         ad-do-it)))))
(my-unpop-to-mark-advice)

C--ctrl + -(负号),它被绑定到了我的撤销操作上。我能否将其更改为其他按键绑定? - alper
是的,但这是由于您自己的配置引起的(因为它不是默认绑定),所以您应该找出您在哪里进行了配置。请确保您没有输入 C-_,因为这是 undo 的默认绑定,在某些键盘布局中 _- 的 shifted 版本。 - phils
在我的键盘上,“撤销”绑定到“C--”,我不确定为什么。 - alper

5
这里有一个函数可以实现这个功能:
(defun unpop-to-mark-command ()
  "Unpop off mark ring into the buffer's actual mark.
Does not set point.  Does nothing if mark ring is empty."
  (interactive)
  (let ((num-times (if (equal last-command 'pop-to-mark-command) 2
                     (if (equal last-command 'unpop-to-mark-command) 1
                       (error "Previous command was not a (un)pop-to-mark-command")))))
    (dotimes (x num-times)
      (when mark-ring
        (setq mark-ring (cons (copy-marker (mark-marker)) mark-ring))
        (set-marker (mark-marker) (+ 0 (car (last mark-ring))) (current-buffer))
        (when (null (mark t)) (ding))
        (setq mark-ring (nbutlast mark-ring))
        (goto-char (mark t)))
      (deactivate-mark))))

有没有关于将其与现有的弹出标记机制相关联的明智绑定的想法?例如,如果您从“C-u C-SPC C-SPC C-SPC ...”开始并超过了目标,那么最好有一些简单的方法来反转方向。也许可以使用set-mark-command的before-advice使C-u C-SPC作为两个功能之间的切换? - phils
2
已排序:https://dev59.com/_3A75IYBdhLWcg3wRWkU#5117076 - phils

5

有两个标记环:一个是当前缓冲区的本地环境,另一个是所有缓冲区共享的全局环境。

默认情况下,在 Icicles(在 Icicle 全局辅助模式中):

  • C-- C-SPC 可让您在本地标记之间切换
  • C-- C-x C-SPC 可让您在全局标记之间切换

IOW,使用负前缀 arg,C-SPCC-x C-SPC 进行导航。如果没有,则它们将执行其正常操作(分别为 set-mark-commandpop-global-mark)。

导航方式如下:

  • 位置可用作自动完成候选项:文本为标记行的文本。
  • 完成:可以使用子字符串、正则表达式、前缀、模糊匹配(各种类型)。
  • 您可以在任何自动完成选项之间循环,或直接转到其中任何一个。

在输入期间可用的键包括:

  • up, down -- 在*Completions*中循环候选项,而不导航到它们的位置

  • C-up, C-down -- 循环导航到每个位置

  • C-RET, C-mouse-2 -- 直接转到当前/点击的候选项(例如在*Completions*中)

  • RET, mouse-2 -- 与上一条相同(转到候选项),但结束命令(完成)

  • S-TAB -- apropos-complete(子字符串/正则表达式)

  • TAB -- 前缀或模糊完成


2

虽然它不能完全满足您的要求,但是您可以尝试查找一个叫做marker-visit.el的软件包,它可以让您按照“缓冲区位置顺序”导航当前缓冲区中的标记。以下是该文件的一部分:

;;; Commentary:

;; This file provides a simple way to navigate among marks in a
;; buffer.  C-u C-SPC is similar, but takes you haphazardly around the
;; buffer.  Setting bookmarks is a lot of extra work if you just want
;; to jump around your buffer quickly; plus, you have to come up with
;; a name for every bookmark.

;; All the marks you've left while editing a buffer serve as bread
;; crumb trails of areas in the buffer you've edited.  It is
;; convenient to navigate back and forth among these marks in order.
;; This file provides two methods to do just that, marker-visit-prev
;; and marker-visit-next.  These two functions will take you, from
;; point, to the nearest mark in either direction.  The function
;; marker-visit-truncate-mark-ring will truncate the mark ring.

;; The marks you can visit in a buffer consist of: "the mark" plus the
;; contents of the mark-ring.

我将[S-up]和[S-down]绑定到marker-visit-prev和marker-visit-next。

如果你真的想/需要按照mark-ring的当前顺序导航,那么你可以查看pop-to-mark-command和pop-mark函数,并实现自己的版本以相反的方向旋转mark-ring。


啊,抱歉 - 没有意识到这很难找。我怀疑我的配置中收集的一半elisp正在领取每周的养老金。 - Simon
使用Bookmark+,书签可以像标记一样简单--不需要命名等。而且你可以在它们之间循环。请参见自动命名的书签 - Drew
1
现在它也在emacsmirror的Github存储库中:http://github.com/emacsmirror/marker-visit。 - ior3k
我刚刚再次尝试了一下(github版本),在Emacs 24上,它只是擦除了整个环,只保留了之前的两个标记。所以,不行。 - yPhil
能够像Intellij一样具有向前和向后跳转的功能将是非常好的,保留相对于当前位置的某种过去和未来的状态。 marker-visit是否能实现此功能? - Andrea Richiardi

1

手册上说:

变量mark-ring-max指定了标记环中要保留的最大条目数。如果已经存在这么多条目并且又推入了另一个条目,则列表中最早的条目将被丢弃。重复使用“C-u C-'”可以循环浏览当前在环中的位置。

我建议您使用它来限制标记环的大小(例如3或4,我的当前为16)。然后,您可以使用前缀更快地移动它。

此外:

如果您想一遍又一遍地返回相同的位置,则标记环可能不够方便。如果是这样,您可以将位置记录在寄存器中以供以后检索(*参见在寄存器中保存位置:RegPos.)。


包含标记数量有点违背了浏览标记列表的初衷。 - RichieHH

0

关于emacs按键绑定的问题,这里并没有直接的答案。

针对evil用户

我发现了一个适用于evil用户的better-jumper插件。如果你对evil-mode感兴趣,我真诚地建议你使用它。

repository的主页中有详细的使用说明。

(with-eval-after-load 'evil-maps
  (define-key evil-motion-state-map (kbd "C-o") 'better-jumper-jump-backward)
  (define-key evil-motion-state-map (kbd "<C-i>") 'better-jumper-jump-forward))

回到光标的旧位置和前进到新位置非常容易。

此外,您可以使用其hook函数创建新场景,例如更改缓冲区标记旧点等。

对于Emacs键绑定粉丝

我唯一能给出的默认键绑定样式建议是helm-all-mark-rings。它提供了有关标记环的最佳可见性。


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