-
Notifications
You must be signed in to change notification settings - Fork 3
/
lsp-rocks-xref.el
195 lines (161 loc) · 7.37 KB
/
lsp-rocks-xref.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
;;; lsp-rocks-xref.el --- async xref -*- lexical-binding: t; -*-
;; Copyright (C) 2021 vritser
;; Author: vritser <vritser@gmail.com>
;; Keywords: LSP
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;
;;; Code:
(require 'xref)
(cl-defgeneric xref-backend-definitions (backend identifier callback)
"Find definitions of IDENTIFIER.
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-backend-identifier-at-point', or from the table returned by
`xref-backend-identifier-completion-table'.
To create an xref object, call `xref-make'.")
(cl-defgeneric xref-backend-references (_backend identifier callback)
"Find references of IDENTIFIER.
The result must be a list of xref objects. If no references can
be found, return nil.
The default implementation uses `semantic-symref-tool-alist' to
find a search tool; by default, this uses \"find | grep\" in the
current project's main and external roots."
(mapcan
(lambda (dir)
(message "Searching %s..." dir)
(redisplay)
(prog1
(xref-references-in-directory identifier dir)
(message "Searching %s... done" dir)))
(let ((pr (project-current t)))
(cons
(xref--project-root pr)
(project-external-roots pr)))))
(cl-defgeneric xref-backend-implementations (backend identifier callback)
"Find implementations of IDENTIFIER.
The result must be a list of xref objects. If there are multiple possible
implementations, return all of them. If no implementations can be found,
return nil.
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'.")
(cl-defgeneric xref-backend-type-definitions (backend identifier callback)
"Find implementations of IDENTIFIER.
The result must be a list of xref objects. If there are multiple possible
implementations, return all of them. If no implementations can be found,
return nil.
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'.")
(defun xref--show-xref-buffer (xrefs alist)
(let* ((_xrefs
(or
(assoc-default 'fetched-xrefs alist)
xrefs))
(xref-alist (xref--analyze _xrefs))
(dd default-directory)
buf)
(with-current-buffer (get-buffer-create xref-buffer-name)
(setq default-directory dd)
(xref--xref-buffer-mode)
(xref--show-common-initialize xref-alist _xrefs alist)
(pop-to-buffer (current-buffer))
(setq buf (current-buffer)))
(xref--auto-jump-first buf (assoc-default 'auto-jump alist))
buf))
(defun xref-show-definitions-buffer (xrefs alist)
"Show the definitions list in a regular window.
When only one definition found, jump to it right away instead."
(let (buf)
(cond
((not (cdr xrefs))
(xref-pop-to-location (car xrefs)
(assoc-default 'display-action alist)))
(t
(setq buf
(xref--show-xref-buffer xrefs
(cons (cons 'fetched-xrefs xrefs)
alist)))
(xref--auto-jump-first buf (assoc-default 'auto-jump alist))
buf))))
(defun xref--show-xrefs (xrefs display-action &optional _always-show-list)
(xref--push-markers)
(funcall xref-show-xrefs-function xrefs
`((window . ,(selected-window))
(display-action . ,display-action)
(auto-jump . ,xref-auto-jump-to-first-xref))))
(defun xref--show-defs (xrefs display-action)
(xref--push-markers)
(funcall xref-show-definitions-function xrefs
`((window . ,(selected-window))
(display-action . ,display-action)
(auto-jump . ,xref-auto-jump-to-first-definition))))
(defun xref--create-fetcher (input kind arg callback)
"Return an xref list fetcher function.
It revisits the saved position and delegates the finding logic to
the xref backend method indicated by KIND and passes ARG to it."
(let* ((orig-buffer (current-buffer))
(orig-position (point))
(backend (xref-find-backend))
(method (intern (format "xref-backend-%s" kind))))
(save-excursion
;; Xref methods are generally allowed to depend on the text
;; around point, not just on their explicit arguments.
;;
;; There is only so much we can do, however, to recreate that
;; context, given that the user is free to change the buffer
;; contents freely in the meantime.
(when (buffer-live-p orig-buffer)
(set-buffer orig-buffer)
(ignore-errors (goto-char orig-position)))
(if (member (symbol-name backend) '("elisp" "etags"))
(funcall callback (funcall method backend arg))
(funcall method backend arg callback)))))
(defun xref--find-xrefs (input kind arg display-action)
(let ((callback (lambda (display-action xrefs)
(xref--show-xrefs xrefs display-action))))
(xref--create-fetcher input kind arg
(apply-partially callback display-action))))
(defun xref--find-definitions (id display-action)
(let ((callback (lambda (display-action xrefs)
(xref--show-defs xrefs display-action))))
(xref--create-fetcher id 'definitions id
(apply-partially callback display-action))))
;;;###autoload
(defun xref-find-implementations (identifier)
"Find implementations to the identifier at point.
This command might prompt for the identifier as needed, perhaps
offering the symbol at point as the default.
With prefix argument, or if `xref-prompt-for-identifier' is t,
always prompt for the identifier. If `xref-prompt-for-identifier'
is nil, prompt only if there's no usable symbol at point."
(interactive (list (xref--read-identifier "Find implementations of: ")))
(xref--find-xrefs identifier 'implementations identifier nil))
;;;###autoload
(defun xref-find-type-definitions (identifier)
"Find implementations to the identifier at point.
This command might prompt for the identifier as needed, perhaps
offering the symbol at point as the default.
With prefix argument, or if `xref-prompt-for-identifier' is t,
always prompt for the identifier. If `xref-prompt-for-identifier'
is nil, prompt only if there's no usable symbol at point."
(interactive (list (xref--read-identifier "Find type definitions of: ")))
(xref--find-xrefs identifier 'type-definitions identifier nil))
(provide 'lsp-rocks-xref)
;;; lsp-rocks-xref.el ends here