-
Notifications
You must be signed in to change notification settings - Fork 26
/
citre-xref-adapter.el
170 lines (135 loc) · 5.92 KB
/
citre-xref-adapter.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
;;; citre-tags.el --- Tags file backend -*- lexical-binding: t -*-
;; Copyright (C) 2024 Hao Wang
;; Author: Hao Wang <[email protected]>
;; Maintainer: Hao Wang <[email protected]>
;; Created: 14 April 2024
;; Keywords: convenience, tools
;; Homepage: https://github.com/universal-ctags/citre
;; Version: 0.4.1
;; Package-Requires: ((emacs "26.1"))
;; This file is NOT part of GNU Emacs.
;; 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 <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Xref backend adapter to convert an xref backend to a Citre backend. It also
;; bundles an eglot backend as an example.
;;; Code:
;; To see the outline of this file, run M-x outline-minor-mode and
;; then press C-c @ C-t. To also show the top-level functions and
;; variable declarations in each section, run M-x occur with the
;; following query: ^;;;;* \|^(
;;;; Libraries
(require 'citre-backend-interface)
;; We only use `citre-tags--symbol-at-point' in this module. TODO: We really
;; shouldn't need this dependency. We should extract a module for getting
;; symbol at point.
(require 'citre-tags)
(require 'cl-lib)
(require 'xref)
;;;; User Options
;;;; Internals
(defun citre--get-tags-by-xref-method
(method backend &optional symbol-atpt-fn)
"Find tags for identifier at point by xref backend BACKEND using METHOD.
METHOD is a `xref-backend-*' function. The result is a list of
tags, or nil if tags couldn't be found or created.
When SYMBOL-ATPT-FN is non-nil, the symbol name captured by this
function is used as names of the tags."
(let* ((xref-id (xref-backend-identifier-at-point backend))
(id (if symbol-atpt-fn (funcall symbol-atpt-fn) xref-id))
(id-no-props (substring-no-properties id))
(objs (funcall method backend xref-id)))
(cl-loop for obj in objs
for tag = (citre-xref-object-to-tag obj id-no-props)
if tag collect tag)))
(defun citre--xref-definitions (backend &optional symbol-atpt-fn)
"Find definitions of identifier at point by xref backend BACKEND.
The result is a list of tags, or nil if definitions couldn't be
found or tags couldn't be created.
When SYMBOL-ATPT-FN is non-nil, the symbol name captured by this
function is used as names of the tags."
(citre--get-tags-by-xref-method
#'xref-backend-definitions backend symbol-atpt-fn))
(defun citre--xref-references (backend &optional symbol-atpt-fn)
"Find references of identifier at point by xref backend BACKEND.
The result is a list of tags, or nil if references couldn't be
found or tags couldn't be created.
When SYMBOL-ATPT-FN is non-nil, the symbol name captured by this
function is used as names of the tags."
(let ((tags (citre--get-tags-by-xref-method
#'xref-backend-references backend symbol-atpt-fn)))
(dolist (tag tags)
(citre-set-tag-field 'extras "reference" tag))
tags))
;;;; APIs
(cl-defun citre-xref-backend-to-citre-backend
(backend usable-p &key symbol-atpt-fn after-jump-fn
no-definition-backend no-reference-backend)
"Convert xref backend BACKEND to Citre backend.
USABLE-P is a predicate telling if BACKEND is usable in the
buffer.
Citre backends for finding definitions and references are
registered, which can be disabled respectively by keyword
arguments NO-DEFINITION-BACKEND and NO-REFERENCE-BACKEND.
Some xref backends doesn't grab symbol at point correctly, e.g.,
symbols get by `eglot' are always \"LSP identifier at point\",
and the actual information is hidden in its text properties. For
these backends, provide a SYMBOL-ATPT-FN to get the actual symbol
name at point.
When jumping to a definition, you may want to initialize the
backend to make it work for further jumping. Provide a
AFTER-JUMP-FN for this."
(citre-backend-create
:usable-probe usable-p
:symbol-at-point-fn (or symbol-atpt-fn
(lambda ()
(when (funcall usable-p)
(xref-backend-identifier-at-point backend))))
:defs-fn (unless no-definition-backend
(lambda ()
(when (funcall usable-p)
(citre--xref-definitions backend symbol-atpt-fn))))
:refs-fn (unless no-reference-backend
(lambda ()
(when (funcall usable-p)
(citre--xref-references backend symbol-atpt-fn))))
:after-jump-fn after-jump-fn))
;;;; Implemented xref adapters
;;;;; Eglot
(declare-function eglot-managed-p "eglot" ())
(declare-function eglot-ensure "eglot" ())
(defun citre--eglot-usable-p ()
"See if eglot is usable in current buffer."
(when (featurep 'eglot)
(eglot-managed-p)))
(defun citre--eglot-after-jump ()
"Make sure eglot is enabled after jump."
(eglot-ensure)
;; It seems eglot is actually made usable in `post-command-hook'. We need
;; this as `citre-peek-through' may get defs/refs in a new buffer without
;; executing any command.
(run-hooks 'post-command-hook))
(defvar citre-eglot-backend
(citre-xref-backend-to-citre-backend
'eglot #'citre--eglot-usable-p
:symbol-atpt-fn #'citre-tags-symbol-at-point
:after-jump-fn #'citre--eglot-after-jump)
"Eglot backend.")
(citre-register-backend 'eglot citre-eglot-backend)
(provide 'citre-xref-adapter)
;; Local Variables:
;; indent-tabs-mode: nil
;; outline-regexp: ";;;;* "
;; fill-column: 79
;; emacs-lisp-docstring-fill-column: 65
;; sentence-end-double-space: t
;; End:
;;; citre-xref-adapter.el ends here