From 90d3fdaffcc9b13103d6a4fa0afa972ae214739c Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 6 Feb 2026 05:56:31 +0200 Subject: [PATCH] Allow using xref-find-references without visiting a tags table * lisp/progmodes/xref.el (xref-find-backend): Allow returning nil (bug#43086). (xref-backend-definitions, xref-backend-apropos): Signal user-error when no backend is available. The error text suggests a few built-in Xref backends. (xref-backend-identifier-completion-table): Default to nil. (xref--no-backend-available): New helper function. * lisp/progmodes/etags.el (etags--xref-backend): Return nil when no tags table is visited. --- etc/NEWS | 5 +++++ lisp/progmodes/etags.el | 4 +++- lisp/progmodes/xref.el | 23 ++++++++++++++--------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 9e5ac796697..3795913d99c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3701,6 +3701,11 @@ So now it is even more important that any calls to 'with-help-window' (recommended) to 'with-output-to-temp-buffer' are done after. It was the recommended way to use it previously as well, but less critically so. +** Xref commands don't automatically suggest to visit a tags table anymore. +When no tags file is loaded, symbol completion just won't provide any +suggestions. So the 'M-?' command now works without a tags table. And +the 'M-.' will show a message describing the several built-in options +that will provide an Xref backend when used. * Lisp Changes in Emacs 31.1 diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el index 79cfb91caa9..aa83022fe47 100644 --- a/lisp/progmodes/etags.el +++ b/lisp/progmodes/etags.el @@ -2115,7 +2115,9 @@ file name, add `tag-partial-file-name-match-p' to the list value.") :version "28.1") ;;;###autoload -(defun etags--xref-backend () 'etags) +(defun etags--xref-backend () + (when (or tags-table-list tags-file-name) + 'etags)) (cl-defmethod xref-backend-identifier-at-point ((_backend (eql 'etags))) (find-tag--default)) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 84a3fa4dfba..1e51b23eaff 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -247,11 +247,9 @@ generic functions.") ;;;###autoload (defun xref-find-backend () - (or - (run-hook-with-args-until-success 'xref-backend-functions) - (user-error "No Xref backend available"))) + (run-hook-with-args-until-success 'xref-backend-functions)) -(cl-defgeneric xref-backend-definitions (backend identifier) +(cl-defgeneric xref-backend-definitions (_backend _identifier) "Find definitions of IDENTIFIER. The result must be a list of xref objects. If IDENTIFIER @@ -264,7 +262,8 @@ IDENTIFIER can be any string returned by `xref-backend-identifier-at-point', or from the table returned by `xref-backend-identifier-completion-table'. -To create an xref object, call `xref-make'.") +To create an xref object, call `xref-make'." + (xref--no-backend-available)) (cl-defgeneric xref-backend-references (_backend identifier) "Find references of IDENTIFIER. @@ -285,12 +284,13 @@ The default implementation uses `xref-references-in-directory'." (xref--project-root pr) (project-external-roots pr)))))) -(cl-defgeneric xref-backend-apropos (backend pattern) +(cl-defgeneric xref-backend-apropos (_backend _pattern) "Find all symbols that match PATTERN string. The second argument has the same meaning as in `apropos'. If BACKEND is implemented in Lisp, it can use -`xref-apropos-regexp' to convert the pattern to regexp.") +`xref-apropos-regexp' to convert the pattern to regexp." + (xref--no-backend-available)) (cl-defgeneric xref-backend-identifier-at-point (_backend) "Return the relevant identifier at point. @@ -306,8 +306,9 @@ recognize and then delegate the work to an external process." (let ((thing (thing-at-point 'symbol))) (and thing (substring-no-properties thing)))) -(cl-defgeneric xref-backend-identifier-completion-table (backend) - "Return the completion table for identifiers.") +(cl-defgeneric xref-backend-identifier-completion-table (_backend) + "Return the completion table for identifiers." + nil) (cl-defgeneric xref-backend-identifier-completion-ignore-case (_backend) "Return t if case is not significant in identifier completion." @@ -329,6 +330,10 @@ KEY extracts the key from an element." (cl-loop for key being hash-keys of table using (hash-values value) collect (cons key (nreverse value))))) +(defun xref--no-backend-available () + (user-error + "No Xref backend. Try `M-x eglot', `M-x visit-tags-table', or `M-x etags-regen-mode'.")) + (defun xref--insert-propertized (props &rest strings) "Insert STRINGS with text properties PROPS." (let ((start (point)))