Use generic dispatch for xref backends
* lisp/progmodes/xref.el (xref-backend-functions): New variable. (xref-find-function): Remove. (xref-find-backend) (xref--etags-backend): New functions. (xref-identifier-at-point-function) (xref-identifier-completion-table-function): Remove. (xref-backend-definitions, xref-backend-references) (xref-backend-apropos, xref-backend-identifier-at-point) (xref-backend-identifier-completion-table): New generic functions. * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): Add `elisp--xref-backend' to the beginning of `xref-backend-functions', locally. Delete references to removed functions and vars. (elisp-xref-find): Remove. (elisp--xref-backend): New function. (elisp--xref-find-references, elisp--xref-find-apropos) (elisp--xref-identifier-completion-table): Turn into appropriately named generic methods. * lisp/progmodes/etags.el (etags-xref-find): Remove. (xref-backend-identifier-completion-table) (xref-backend-references, xref-backend-definitions) (xref-backend-apropos): New generic methods.
This commit is contained in:
parent
31f6e93933
commit
246d6605f7
3 changed files with 87 additions and 72 deletions
|
@ -228,8 +228,7 @@ Blank lines separate paragraphs. Semicolons start comments.
|
|||
|
||||
\\{emacs-lisp-mode-map}"
|
||||
:group 'lisp
|
||||
(defvar xref-find-function)
|
||||
(defvar xref-identifier-completion-table-function)
|
||||
(defvar xref-backend-functions)
|
||||
(defvar project-library-roots-function)
|
||||
(lisp-mode-variables nil nil 'elisp)
|
||||
(add-hook 'after-load-functions #'elisp--font-lock-flush-elisp-buffers)
|
||||
|
@ -239,9 +238,7 @@ Blank lines separate paragraphs. Semicolons start comments.
|
|||
(setq imenu-case-fold-search nil)
|
||||
(add-function :before-until (local 'eldoc-documentation-function)
|
||||
#'elisp-eldoc-documentation-function)
|
||||
(setq-local xref-find-function #'elisp-xref-find)
|
||||
(setq-local xref-identifier-completion-table-function
|
||||
#'elisp--xref-identifier-completion-table)
|
||||
(add-hook 'xref-backend-functions #'elisp--xref-backend nil t)
|
||||
(setq-local project-library-roots-function #'elisp-library-roots)
|
||||
(add-hook 'completion-at-point-functions
|
||||
#'elisp-completion-at-point nil 'local))
|
||||
|
@ -588,21 +585,7 @@ It can be quoted, or be inside a quoted form."
|
|||
(declare-function xref-make "xref" (summary location))
|
||||
(declare-function xref-collect-references "xref" (symbol dir))
|
||||
|
||||
(defun elisp-xref-find (action id)
|
||||
(require 'find-func)
|
||||
;; FIXME: use information in source near point to filter results:
|
||||
;; (dvc-log-edit ...) - exclude 'feature
|
||||
;; (require 'dvc-log-edit) - only 'feature
|
||||
;; Semantic may provide additional information
|
||||
(pcase action
|
||||
(`definitions
|
||||
(let ((sym (intern-soft id)))
|
||||
(when sym
|
||||
(elisp--xref-find-definitions sym))))
|
||||
(`references
|
||||
(elisp--xref-find-references id))
|
||||
(`apropos
|
||||
(elisp--xref-find-apropos id))))
|
||||
(defun elisp--xref-backend () 'elisp)
|
||||
|
||||
;; WORKAROUND: This is nominally a constant, but the text properties
|
||||
;; are not preserved thru dump if use defconst. See bug#21237.
|
||||
|
@ -638,7 +621,17 @@ Each function should return a list of xrefs, or nil; the first
|
|||
non-nil result supercedes the xrefs produced by
|
||||
`elisp--xref-find-definitions'.")
|
||||
|
||||
;; FIXME: name should be singular; match xref-find-definition
|
||||
(cl-defmethod xref-backend-definitions ((_backend (eql elisp)) identifier)
|
||||
(require 'find-func)
|
||||
;; FIXME: use information in source near point to filter results:
|
||||
;; (dvc-log-edit ...) - exclude 'feature
|
||||
;; (require 'dvc-log-edit) - only 'feature
|
||||
;; Semantic may provide additional information
|
||||
;;
|
||||
(let ((sym (intern-soft identifier)))
|
||||
(when sym
|
||||
(elisp--xref-find-definitions sym))))
|
||||
|
||||
(defun elisp--xref-find-definitions (symbol)
|
||||
;; The file name is not known when `symbol' is defined via interactive eval.
|
||||
(let (xrefs)
|
||||
|
@ -805,7 +798,7 @@ non-nil result supercedes the xrefs produced by
|
|||
(declare-function project-roots "project")
|
||||
(declare-function project-current "project")
|
||||
|
||||
(defun elisp--xref-find-references (symbol)
|
||||
(cl-defmethod xref-backend-references ((_backend (eql elisp)) symbol)
|
||||
"Find all references to SYMBOL (a string) in the current project."
|
||||
(cl-mapcan
|
||||
(lambda (dir)
|
||||
|
@ -815,7 +808,7 @@ non-nil result supercedes the xrefs produced by
|
|||
(project-roots pr)
|
||||
(project-library-roots pr)))))
|
||||
|
||||
(defun elisp--xref-find-apropos (regexp)
|
||||
(cl-defmethod xref-backend-apropos ((_backend (eql elisp)) regexp)
|
||||
(apply #'nconc
|
||||
(let (lst)
|
||||
(dolist (sym (apropos-internal regexp))
|
||||
|
@ -832,7 +825,7 @@ non-nil result supercedes the xrefs produced by
|
|||
(facep sym)))
|
||||
'strict))
|
||||
|
||||
(defun elisp--xref-identifier-completion-table ()
|
||||
(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql elisp)))
|
||||
elisp--xref-identifier-completion-table)
|
||||
|
||||
(cl-defstruct (xref-elisp-location
|
||||
|
|
|
@ -2084,17 +2084,12 @@ for \\[find-tag] (which see)."
|
|||
|
||||
(defvar etags-xref-find-definitions-tag-order '(tag-exact-match-p
|
||||
tag-implicit-name-match-p)
|
||||
"Tag order used in `etags-xref-find' to look for definitions.")
|
||||
"Tag order used in `xref-backend-definitions' to look for definitions.")
|
||||
|
||||
;;;###autoload
|
||||
(defun etags-xref-find (action id)
|
||||
(pcase action
|
||||
(`definitions (etags--xref-find-definitions id))
|
||||
(`references (etags--xref-find-references id))
|
||||
(`apropos (etags--xref-find-definitions id t))))
|
||||
(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql etags)))
|
||||
(tags-lazy-completion-table))
|
||||
|
||||
(defun etags--xref-find-references (symbol)
|
||||
;; TODO: Merge together with the Elisp impl.
|
||||
(cl-defmethod xref-backend-references ((_backend (eql etags)) symbol)
|
||||
(cl-mapcan
|
||||
(lambda (dir)
|
||||
(xref-collect-references symbol dir))
|
||||
|
@ -2103,6 +2098,12 @@ for \\[find-tag] (which see)."
|
|||
(project-roots pr)
|
||||
(project-library-roots pr)))))
|
||||
|
||||
(cl-defmethod xref-backend-definitions ((_backend (eql etags)) symbol)
|
||||
(etags--xref-find-definitions symbol))
|
||||
|
||||
(cl-defmethod xref-backend-apropos ((_backend (eql etags)) symbol)
|
||||
(etags--xref-find-definitions symbol t))
|
||||
|
||||
(defun etags--xref-find-definitions (pattern &optional regexp?)
|
||||
;; This emulates the behaviour of `find-tag-in-order' but instead of
|
||||
;; returning one match at a time all matches are returned as list.
|
||||
|
|
|
@ -23,14 +23,21 @@
|
|||
;; referencing commands, in particular "find-definition".
|
||||
;;
|
||||
;; Some part of the functionality must be implemented in a language
|
||||
;; dependent way and that's done by defining `xref-find-function',
|
||||
;; `xref-identifier-at-point-function' and
|
||||
;; `xref-identifier-completion-table-function', which see.
|
||||
;; dependent way and that's done by defining an xref backend.
|
||||
;;
|
||||
;; A major mode should make these variables buffer-local first.
|
||||
;; That consists of a constructor function, which should return a
|
||||
;; backend value, and a set of implementations for the generic
|
||||
;; functions:
|
||||
;;
|
||||
;; `xref-find-function' can be called in several ways, see its
|
||||
;; description. It has to operate with "xref" and "location" values.
|
||||
;; `xref-backend-identifier-at-point',
|
||||
;; `xref-backend-identifier-completion-table',
|
||||
;; `xref-backend-definitions', `xref-backend-references',
|
||||
;; `xref-backend-apropos', which see.
|
||||
;;
|
||||
;; A major mode would normally use `add-hook' to add the backend
|
||||
;; constructor to `xref-backend-functions'.
|
||||
;;
|
||||
;; The last three methods operate with "xref" and "location" values.
|
||||
;;
|
||||
;; One would usually call `make-xref' and `xref-make-file-location',
|
||||
;; `xref-make-buffer-location' or `xref-make-bogus-location' to create
|
||||
|
@ -46,12 +53,11 @@
|
|||
;; Each identifier must be represented as a string. Implementers can
|
||||
;; use string properties to store additional information about the
|
||||
;; identifier, but they should keep in mind that values returned from
|
||||
;; `xref-identifier-completion-table-function' should still be
|
||||
;; `xref-backend-identifier-completion-table' should still be
|
||||
;; distinct, because the user can't see the properties when making the
|
||||
;; choice.
|
||||
;;
|
||||
;; See the functions `etags-xref-find' and `elisp-xref-find' for full
|
||||
;; examples.
|
||||
;; See the etags and elisp-mode implementations for full examples.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
@ -195,35 +201,46 @@ LENGTH is the match length, in characters."
|
|||
|
||||
;;; API
|
||||
|
||||
(declare-function etags-xref-find "etags" (action id))
|
||||
(declare-function tags-lazy-completion-table "etags" ())
|
||||
;; We make the etags backend the default for now, until something
|
||||
;; better comes along.
|
||||
(defvar xref-backend-functions (list #'xref--etags-backend)
|
||||
"Special hook to find the xref backend for the current context.
|
||||
Each functions on this hook is called in turn with no arguments
|
||||
and should return either nil to mean that it is not applicable,
|
||||
or an xref backend, which is a value to be used to dispatch the
|
||||
generic functions.")
|
||||
|
||||
;; For now, make the etags backend the default.
|
||||
(defvar xref-find-function #'etags-xref-find
|
||||
"Function to look for cross-references.
|
||||
It can be called in several ways:
|
||||
(defun xref-find-backend ()
|
||||
(run-hook-with-args-until-success 'xref-backend-functions))
|
||||
|
||||
(definitions IDENTIFIER): Find definitions of IDENTIFIER. The
|
||||
result must be a list of xref objects. If IDENTIFIER contains
|
||||
sufficient information to determine a unique definition, returns
|
||||
only that definition. If there are multiple possible definitions,
|
||||
return all of them. If no definitions can be found, return nil.
|
||||
(defun xref--etags-backend () 'etags)
|
||||
|
||||
(references IDENTIFIER): Find references of IDENTIFIER. The
|
||||
result must be a list of xref objects. If no references can be
|
||||
found, return nil.
|
||||
(cl-defgeneric xref-backend-definitions (backend identifier)
|
||||
"Find definitions of IDENTIFIER.
|
||||
|
||||
(apropos PATTERN): Find all symbols that match PATTERN. PATTERN
|
||||
is a regexp.
|
||||
The result must be a list of xref objects. If IDENTIFIER
|
||||
contains sufficient information to determine a unique definition,
|
||||
return only that definition. If there are multiple possible
|
||||
definitions, return all of them. If no definitions can be found,
|
||||
return nil.
|
||||
|
||||
IDENTIFIER can be any string returned by
|
||||
`xref-identifier-at-point-function', or from the table returned
|
||||
by `xref-identifier-completion-table-function'.
|
||||
`xref-backend-identifier-at-point', or from the table returned by
|
||||
`xref-backend-identifier-completion-table'.
|
||||
|
||||
To create an xref object, call `xref-make'.")
|
||||
|
||||
(defvar xref-identifier-at-point-function #'xref-default-identifier-at-point
|
||||
"Function to get the relevant identifier at point.
|
||||
(cl-defgeneric xref-backend-references (backend identifier)
|
||||
"Find references of IDENTIFIER.
|
||||
The result must be a list of xref objects. If no references can
|
||||
be found, return nil.")
|
||||
|
||||
(cl-defgeneric xref-backend-apropos (backend pattern)
|
||||
"Find all symbols that match PATTERN.
|
||||
PATTERN is a regexp")
|
||||
|
||||
(cl-defgeneric xref-backend-identifier-at-point (_backend)
|
||||
"Return the relevant identifier at point.
|
||||
|
||||
The return value must be a string or nil. nil means no
|
||||
identifier at point found.
|
||||
|
@ -231,16 +248,14 @@ identifier at point found.
|
|||
If it's hard to determine the identifier precisely (e.g., because
|
||||
it's a method call on unknown type), the implementation can
|
||||
return a simple string (such as symbol at point) marked with a
|
||||
special text property which `xref-find-function' would recognize
|
||||
and then delegate the work to an external process.")
|
||||
|
||||
(defvar xref-identifier-completion-table-function #'tags-lazy-completion-table
|
||||
"Function that returns the completion table for identifiers.")
|
||||
|
||||
(defun xref-default-identifier-at-point ()
|
||||
special text property which e.g. `xref-backend-definitions' would
|
||||
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)
|
||||
"Returns the completion table for identifiers.")
|
||||
|
||||
|
||||
;;; misc utilities
|
||||
(defun xref--alistify (list key test)
|
||||
|
@ -690,7 +705,8 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
|
|||
|
||||
(defun xref--read-identifier (prompt)
|
||||
"Return the identifier at point or read it from the minibuffer."
|
||||
(let ((id (funcall xref-identifier-at-point-function)))
|
||||
(let* ((backend (xref-find-backend))
|
||||
(id (xref-backend-identifier-at-point backend)))
|
||||
(cond ((or current-prefix-arg
|
||||
(not id)
|
||||
(xref--prompt-p this-command))
|
||||
|
@ -700,7 +716,7 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
|
|||
"[ :]+\\'" prompt))
|
||||
id)
|
||||
prompt)
|
||||
(funcall xref-identifier-completion-table-function)
|
||||
(xref-backend-identifier-completion-table backend)
|
||||
nil nil nil
|
||||
'xref--read-identifier-history id))
|
||||
(t id))))
|
||||
|
@ -709,7 +725,9 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)."
|
|||
;;; Commands
|
||||
|
||||
(defun xref--find-xrefs (input kind arg window)
|
||||
(let ((xrefs (funcall xref-find-function kind arg)))
|
||||
(let ((xrefs (funcall (intern (format "xref-backend-%s" kind))
|
||||
(xref-find-backend)
|
||||
arg)))
|
||||
(unless xrefs
|
||||
(user-error "No %s found for: %s" (symbol-name kind) input))
|
||||
(xref--show-xrefs xrefs window)))
|
||||
|
@ -824,6 +842,8 @@ tools are used, and when."
|
|||
(cl-mapcan (lambda (hit) (xref--collect-matches
|
||||
hit (format "\\_<%s\\_>" (regexp-quote symbol))))
|
||||
hits)
|
||||
;; TODO: Implement "lightweight" buffer visiting, so that we
|
||||
;; don't have to kill them.
|
||||
(mapc #'kill-buffer
|
||||
(cl-set-difference (buffer-list) orig-buffers)))))
|
||||
|
||||
|
@ -856,6 +876,7 @@ IGNORES is a list of glob patterns."
|
|||
(unwind-protect
|
||||
(cl-mapcan (lambda (hit) (xref--collect-matches hit regexp))
|
||||
(nreverse hits))
|
||||
;; TODO: Same as above.
|
||||
(mapc #'kill-buffer
|
||||
(cl-set-difference (buffer-list) orig-buffers)))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue