How to move forward and backward in Emacs' mark ring
In Emacs, C-u C-SPC
will "jump to the mark, and set the mark from
position popped off the local mark ring". Is there a way to g开发者_StackOverflowo the opposite way around the mark ring? Say you have typed C-u C-SPC several times and want to go back to a mark you have seen without going all the way around the ring.
Unlike previous answers, this one does only exactly what was asked: the reverse of C-u C-SPC. I find it the most useful.
(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))))))
Comparing to the answer by scottfrazer, this command has a subtle difference of how it moves the cursor and mark, which more accurately mirrors C-u C-spc, and it does not require that the previous command was a unpop/pop-to-mark-command.
Here's a solution I just finished spending way too much time on. The difference between this and the other solutions is it works across buffers, ie it works on the 'global-mark-ring'. My goal was to emulate history browsing similar to Eclipse or IntelliJ. I bind it to M-left and M-right, obviously you can choose different keys for this.
(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))
Following up my comment to scottfrazer's very handy solution, here's some advice which works in conjunction with that to make it easy to reverse directions around the mark ring at will, without the need to use a different key-binding for each direction.
I use cua-selection-mode
, so for me C-SPC is bound to cua-set-mark
, but I've written this as a macro so as to advise whichever function is bound to C-SPC, and verified that it works with the default set-mark-command
.
To unpop, simply supply a negative prefix argument. e.g. C-- C-SPC
One of the nice things about cua-set-mark
is that after an initial C-u C-SPC, you can continue to pop successive marks with just C-SPC, and I've included that behaviour here: After an initial C-- C-SPC you can continue un-popping with just C-SPC. To reverse the direction again and call pop-to-mark
, simply supply a positive argument once more with C-u C-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)
Here's a function to do it:
(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))))
There are two rings of markers: one local to the current buffer and one global among all buffers.
By default, in Icicles (in Icicle global minor mode):
C-- C-SPC
lets you trip among the local markersC-- C-x C-SPC
lets you trip among the global markers
IOW, with a negative prefix arg, C-SPC
and C-x C-SPC
navigate. Without it they just do what they normally do (set-mark-command
and pop-global-mark
, respectively).
Navigating works as follows:
- Locations are available as completion candidates: the text is that of the marker's line.
- Completion: you can use substring, regexp, prefix, fuzzy (various kinds).
- You can cycle among any of the completion candidates, or go to any of them directly.
Keys available during completion include:
up
,down
-- cycle among candidates in*Completions*
, without navigating to their locationsC-up
,C-down
-- cycle, navigating to each location in turnC-RET
,C-mouse-2
-- go directly to the current/clicked candidate (e.g. in*Completions*
)RET
,mouse-2
-- same as previous (go to candidate), but end the command (done)S-TAB
-- apropos-complete (substring/regexp)TAB
-- prefix or fuzzy complete
It doesn't do exactly what you're asking for, but it might be worth looking for a package called marker-visit.el which lets you navigate the marks in the current buffer in 'buffer position order'. From that file:
;;; 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.
I bind [S-up] and [S-down] to marker-visit-prev and marker-visit-next respectively.
If you really want/need to navigate in the order your mark-ring has currently, then you might get somewhere by looking at the functions pop-to-mark-command and pop-mark and implementing your own versions to rotate the mark ring in the opposite direction.
The manual says this:
The variable
mark-ring-max
specifies the maximum number of entries to keep in the mark ring. If that many entries exist and another one is pushed, the earliest one in the list is discarded. Repeating `C-u C-' cycles through the positions currently in the ring.
I suggest you use that to contain the size of the mark ring (to 3 or 4, mine is currently 16). Then you can move around it much faster using prefixes.
Also:
If you want to move back to the same place over and over, the mark ring may not be convenient enough. If so, you can record the position in a register for later retrieval (*note Saving Positions in Registers: RegPos.).
Not directly answer for this question for emacs keybindings.
For evil users
I found better-jumper
for evil users. If you are interested with evil-mode honestly offering to use.
By using How it's offer in it's main 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))
It's really easy to go back and forward to old positions of the cursor.
Additional you can create new scenarios by using it's hook
function like if I change the buffer mark the old point etc.
For emacs key binding fans
Only advice which I can give for default-keybinding style is helm-all-mark-rings
. It gives best visibility about mark-rings.
精彩评论