From 65b34f688c63649a0870dab603c569c1e20007cb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Mattias=20Engdeg=C3=A5rd?= Date: Tue, 19 Oct 2021 13:26:19 +0200 Subject: [PATCH] Add xref forward history (bug#38797) Make it possible to go forward as well as back in the Xref history. The new `xref-go-forward` is bound to `C-M-,`. * lisp/progmodes/etags.el (find-tag-marker-ring-length): Update. (tags-location-ring-length): New. (find-tag-marker-ring): Keep as dummy. (tags-location-ring, tags-reset-tags-tables): Use `tags-location-ring-length` instead of `xref-marker-ring-length`. * lisp/progmodes/xref.el (xref-marker-ring-length, xref-marker-ring) (xref-set-marker-ring-length): Make obsolete. (xref--history, xref-go-forward, xref-forward-history-empty-p): New. (xref-push-marker-stack, xref-pop-marker-stack) (xref-clear-marker-stack, xref-marker-stack-empty-p): Use `xref--history`. * lisp/menu-bar.el (menu-bar-goto-menu): Add Forward entry. * doc/emacs/maintaining.texi (Looking Up Identifiers): Document. * etc/NEWS: Announce. --- doc/emacs/maintaining.texi | 13 ++++-- etc/NEWS | 5 +++ lisp/menu-bar.el | 6 +++ lisp/progmodes/etags.el | 13 +++--- lisp/progmodes/xref.el | 83 +++++++++++++++++++++++--------------- 5 files changed, 79 insertions(+), 41 deletions(-) diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 5b66031b8a6..e28ec5fb437 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -2140,6 +2140,9 @@ Find definition of identifier at mouse click. @item M-, Go back to where you previously invoked @kbd{M-.} and friends (@code{xref-pop-marker-stack}). +@item C-M-, +Go forward to where you previously invoked @kbd{M-,} +(@code{xref-go-forward}). @item M-x xref-etags-mode Switch @code{xref} to use the @code{etags} backend. @end table @@ -2205,14 +2208,16 @@ buffer, but doesn't select any of them. @kindex M-, @findex xref-pop-marker-stack -@vindex xref-marker-ring-length To go back to places @emph{from where} you've displayed the definition, use @kbd{M-,} (@code{xref-pop-marker-stack}). It jumps back to the point of the last invocation of @kbd{M-.}. Thus you can find and examine the definition of something with @kbd{M-.} and then return to -where you were with @kbd{M-,}. @kbd{M-,} allows you to retrace your -steps to a depth determined by the variable -@code{xref-marker-ring-length}, which defaults to 16. +where you were with @kbd{M-,}. + +@kindex C-M-, +@findex xref-go-forward + Go forward to a place from where you previously went back using @kbd{M-,}. +This is useful if you find that you went back too far. @findex xref-etags-mode Some major modes install @code{xref} support facilities that might diff --git a/etc/NEWS b/etc/NEWS index 90ad8d3a46c..2d5276dbd64 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -183,6 +183,11 @@ it with new 'term-{faint,italic,slow-blink,fast-blink}' faces. *** 'project-find-file' and 'project-or-external-find-file' now accept a prefix argument which is interpreted to mean "include all files". ++++ +*** New command 'xref-go-forward'. +It is bound to 'C-M-,' and jumps to the location where 'xref-pop-marker-stack' +was invoked previously. + ** File notifications +++ diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index fafc99eb95e..d1bb8d96779 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -419,6 +419,12 @@ (not (xref-marker-stack-empty-p))) :help "Back to the position of the last search")) + (bindings--define-key menu [xref-forward] + '(menu-item "Forward" xref-go-forward + :visible (and (featurep 'xref) + (not (xref-forward-history-empty-p))) + :help "Forward to the position gone Back from")) + (bindings--define-key menu [xref-apropos] '(menu-item "Find Apropos..." xref-find-apropos :help "Find function/variables whose names match regexp")) diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el index d2ce813daae..318121fbb58 100644 --- a/lisp/progmodes/etags.el +++ b/lisp/progmodes/etags.el @@ -145,7 +145,9 @@ Otherwise, `find-tag-default' is used." :type '(choice (const nil) function)) (define-obsolete-variable-alias 'find-tag-marker-ring-length - 'xref-marker-ring-length "25.1") + 'tags-location-ring-length "25.1") + +(defvar tags-location-ring-length 16) (defcustom tags-tag-face 'default "Face for tags in the output of `tags-apropos'." @@ -180,7 +182,8 @@ Example value: (sexp :tag "Tags to search"))) :version "21.1") -(defvaralias 'find-tag-marker-ring 'xref--marker-ring) +;; Obsolete variable kept for compatibility. We don't use it in any way. +(defvar find-tag-marker-ring (make-ring 16)) (make-obsolete-variable 'find-tag-marker-ring "use `xref-push-marker-stack' or `xref-pop-marker-stack' instead." @@ -191,7 +194,7 @@ Example value: This function receives no arguments and should return the default tags table file to use for the current buffer.") -(defvar tags-location-ring (make-ring xref-marker-ring-length) +(defvar tags-location-ring (make-ring tags-location-ring-length) "Ring of markers which are locations visited by \\[find-tag]. Pop back to the last location with \\[negative-argument] \\[find-tag].") @@ -731,13 +734,13 @@ Returns t if it visits a tags table, or nil if there are no more in the list." (interactive) ;; Clear out the markers we are throwing away. (let ((i 0)) - (while (< i xref-marker-ring-length) + (while (< i tags-location-ring-length) (if (aref (cddr tags-location-ring) i) (set-marker (aref (cddr tags-location-ring) i) nil)) (setq i (1+ i)))) (xref-clear-marker-stack) (setq tags-file-name nil - tags-location-ring (make-ring xref-marker-ring-length) + tags-location-ring (make-ring tags-location-ring-length) tags-table-list nil tags-table-computed-list nil tags-table-computed-list-for nil diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index a198ae349e3..66ac89e72f5 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -341,15 +341,9 @@ backward." (t (goto-char start) nil)))) -;;; Marker stack (M-. pushes, M-, pops) - -(defcustom xref-marker-ring-length 16 - "Length of the xref marker ring. -If this variable is not set through Customize, you must call -`xref-set-marker-ring-length' for changes to take effect." - :type 'integer - :initialize #'custom-initialize-default - :set #'xref-set-marker-ring-length) +;; Dummy variable retained for compatibility. +(defvar xref-marker-ring-length 16) +(make-obsolete-variable 'xref-marker-ring-length nil "29.1") (defcustom xref-prompt-for-identifier '(not xref-find-definitions xref-find-definitions-other-window @@ -420,29 +414,47 @@ or earlier: it can break `dired-do-find-regexp-and-replace'." :version "28.1" :package-version '(xref . "1.2.0")) -(defvar xref--marker-ring (make-ring xref-marker-ring-length) - "Ring of markers to implement the marker stack.") +(defvar xref--history (cons nil nil) + "(BACKWARD-STACK . FORWARD-STACK) of markers to visited Xref locations.") + +(make-obsolete-variable 'xref-marker-ring nil "29.1") -(defun xref-set-marker-ring-length (var val) - "Set `xref-marker-ring-length'. -VAR is the symbol `xref-marker-ring-length' and VAL is the new -value." - (set-default var val) - (if (ring-p xref--marker-ring) - (ring-resize xref--marker-ring val))) +(defun xref-set-marker-ring-length (_var _val) + (declare (obsolete nil "29.1")) + nil) (defun xref-push-marker-stack (&optional m) - "Add point M (defaults to `point-marker') to the marker stack." - (ring-insert xref--marker-ring (or m (point-marker)))) + "Add point M (defaults to `point-marker') to the marker stack. +The future stack is erased." + (push (or m (point-marker)) (car xref--history)) + (dolist (mk (cdr xref--history)) + (set-marker mk nil nil)) + (setcdr xref--history nil)) ;;;###autoload (defun xref-pop-marker-stack () - "Pop back to where \\[xref-find-definitions] was last invoked." + "Go back to the previous position in xref history. +To undo, use \\[xref-go-forward]." (interactive) - (let ((ring xref--marker-ring)) - (when (ring-empty-p ring) - (user-error "Marker stack is empty")) - (let ((marker (ring-remove ring 0))) + (if (null (car xref--history)) + (user-error "At start of xref history") + (let ((marker (pop (car xref--history)))) + (push (point-marker) (cdr xref--history)) + (switch-to-buffer (or (marker-buffer marker) + (user-error "The marked buffer has been deleted"))) + (goto-char (marker-position marker)) + (set-marker marker nil nil) + (run-hooks 'xref-after-return-hook)))) +;; FIXME: rename to `xref-go-back'. + +;;;###autoload +(defun xref-go-forward () + "Got to the point where a previous \\[xref-pop-marker-stack] was invoked." + (interactive) + (if (null (cdr xref--history)) + (user-error "At end of xref history") + (let ((marker (pop (cdr xref--history)))) + (push (point-marker) (car xref--history)) (switch-to-buffer (or (marker-buffer marker) (user-error "The marked buffer has been deleted"))) (goto-char (marker-position marker)) @@ -465,17 +477,23 @@ value." ;; etags.el needs this (defun xref-clear-marker-stack () - "Discard all markers from the marker stack." - (let ((ring xref--marker-ring)) - (while (not (ring-empty-p ring)) - (let ((marker (ring-remove ring))) - (set-marker marker nil nil))))) + "Discard all markers from the xref history." + (dolist (l (list (car xref--history) (cdr xref--history))) + (dolist (m l) + (set-marker m nil nil))) + (setq xref--history (cons nil nil)) + nil) ;;;###autoload (defun xref-marker-stack-empty-p () - "Return t if the marker stack is empty; nil otherwise." - (ring-empty-p xref--marker-ring)) + "Whether the xref back-history is empty." + (null (car xref--history))) +;; FIXME: rename this to `xref-back-history-empty-p'. +;;;###autoload +(defun xref-forward-history-empty-p () + "Whether the xref forward-history is empty." + (null (cdr xref--history))) (defun xref--goto-char (pos) @@ -1494,6 +1512,7 @@ output of this command when the backend is etags." ;;;###autoload (define-key esc-map "," #'xref-pop-marker-stack) ;;;###autoload (define-key esc-map "?" #'xref-find-references) ;;;###autoload (define-key esc-map [?\C-.] #'xref-find-apropos) +;;;###autoload (define-key esc-map [?\C-,] #'xref-go-forward) ;;;###autoload (define-key ctl-x-4-map "." #'xref-find-definitions-other-window) ;;;###autoload (define-key ctl-x-5-map "." #'xref-find-definitions-other-frame) -- 2.39.5