-
Notifications
You must be signed in to change notification settings - Fork 90
/
omnisharp-navigation-actions.el
214 lines (186 loc) · 8.51 KB
/
omnisharp-navigation-actions.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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
;; -*- lexical-binding: t -*-
;; This file 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, or (at your option)
;; any later version.
;; This file 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/>.
(require 'dash)
(defun omnisharp-go-to-definition (&optional other-window)
"Jump to the definition of the symbol under point. With prefix
argument, use another window."
(interactive "P")
(let ((gotodefinition-request (append
'((WantMetadata . t))
(omnisharp--get-request-object))))
(omnisharp--send-command-to-server
"gotodefinition"
gotodefinition-request
(lambda (response)
(omnisharp--prepare-metadata-buffer-if-needed
(omnisharp--get-filename response)
(cdr (assoc 'MetadataSource response))
(lambda (buffer filename)
(omnisharp-go-to-file-line-and-column response
other-window
buffer)))))))
(defun omnisharp--prepare-metadata-buffer-if-needed (filename
metadata-source
callback)
"Prepares metadata buffer if required (if FILENAME is missing and
METADATA-SOURCE is available) and then invokes CALLBACK with either
buffer or FILENAME of the file containing the definition.
Metadata buffer is made readonly and both omnisharp-mode and csharp-mode's
are enabled on this buffer."
(cond
;; when gotodefinition returns FileName for the same
;; metadata buffer as we're in:
;; just return current buffer
((and (boundp 'omnisharp--metadata-source)
(string-equal filename omnisharp--metadata-source))
(funcall callback (current-buffer) nil))
;; when gotodefinition returns an actual filename on the filesystem:
;; navigate to this file
(filename
(funcall callback nil filename))
;; when gotodefinition returns metadata reference:
;; in this case we need to invoke /metadata endpoint to fetch
;; generated C# source for this type from the server (unless we
;; have it already in an existing buffer)
(metadata-source
(let* ((metadata-buffer-name (omnisharp--make-metadata-buffer-name
metadata-source))
(existing-metadata-buffer (get-buffer metadata-buffer-name)))
(if existing-metadata-buffer
;; ok, we have this buffer for this metadata source loaded already
(funcall callback existing-metadata-buffer nil)
;; otherwise we need to actually retrieve metadata-generated source
;; and create a buffer for this type
(omnisharp--send-command-to-server
"metadata"
metadata-source
(lambda (response)
(let ((source (cdr (assoc 'Source response)))
(source-name (cdr (assoc 'SourceName response)))
(new-metadata-buffer (get-buffer-create metadata-buffer-name)))
(with-current-buffer new-metadata-buffer
(insert source)
(csharp-mode)
(omnisharp-mode)
(setq-local omnisharp--metadata-source source-name)
(toggle-read-only 1))
(funcall callback new-metadata-buffer nil)))))))
(t
(message
"Cannot go to definition as none was returned by the API."))))
(defun omnisharp--make-metadata-buffer-name (metadata-source)
"Builds unique buffer name for the given MetadataSource object.
This buffer name assumed to be stable and unique."
(let ((assembly-name (cdr (assoc 'AssemblyName metadata-source)))
(type-name (cdr (assoc 'TypeName metadata-source)))
(project-name (cdr (assoc 'ProjectName metadata-source))))
(concat "*omnisharp-metadata:" project-name ":" assembly-name ":" type-name "*")))
(defun omnisharp-go-to-definition-other-window ()
"Do `omnisharp-go-to-definition' displaying the result in a different window."
(interactive)
(omnisharp-go-to-definition t))
(defun omnisharp-navigate-to-current-file-member
(&optional other-window)
"Show a list of all members in the current file, and jump to the
selected member. With prefix argument, use another window."
(interactive "P")
(omnisharp--send-command-to-server
"currentfilemembersasflat"
(omnisharp--get-request-object)
(lambda (quickfixes)
(omnisharp--choose-and-go-to-quickfix-ido
quickfixes
other-window))))
(defun omnisharp-navigate-to-current-file-member-other-window ()
(interactive)
(omnisharp-navigate-to-current-file-member t))
(defun omnisharp--choose-and-go-to-quickfix-ido
(quickfixes &optional other-window)
"Given a list of QuickFixes in list format (not JSON), displays them
in an completing-read prompt and jumps to the chosen one's
Location.
If OTHER-WINDOW is given, will jump to the result in another window."
(let ((chosen-quickfix
(omnisharp--choose-quickfix-ido
(omnisharp--vector-to-list quickfixes))))
(omnisharp-go-to-file-line-and-column chosen-quickfix
other-window)))
(defun omnisharp--choose-quickfix-ido (quickfixes)
"Given a list of QuickFixes, lets the user choose one using
completing-read. Returns the chosen element."
;; Ido cannot navigate non-unique items reliably. It either gets
;; stuck, or results in that we cannot reliably determine the index
;; of the item. Work around this by prepending the index of all items
;; to their end. This makes them unique.
(let* ((quickfix-choices
(--map-indexed
(let ((this-quickfix-text (cdr (assoc 'Text it))))
(concat "#"
(number-to-string it-index)
"\t"
this-quickfix-text))
quickfixes))
(chosen-quickfix-text
(omnisharp--completing-read
"Go to: "
;; TODO use a hashmap if too slow.
;; This algorithm is two iterations in the worst case
;; scenario.
quickfix-choices))
(chosen-quickfix-index
(cl-position-if (lambda (quickfix-text)
(equal quickfix-text chosen-quickfix-text))
quickfix-choices)))
(nth chosen-quickfix-index quickfixes)))
(defun omnisharp-navigate-to-solution-member (&optional other-window)
(interactive "P")
(let ((filter (omnisharp--read-string
"Enter the start of the symbol to go to: ")))
(omnisharp--send-command-to-server
"findsymbols"
;; gets all symbols. could also filter here but ido doesn't play
;; well with changing its choices
`((Filter . ,filter))
(-lambda ((&alist 'QuickFixes quickfixes))
(omnisharp--choose-and-go-to-quickfix-ido quickfixes other-window)))))
(defun omnisharp-navigate-to-solution-member-other-window ()
(omnisharp-navigate-to-solution-member t))
(defun omnisharp-navigate-to-solution-file (&optional other-window)
(interactive "P")
(omnisharp--send-command-to-server
"gotofile"
nil
(-lambda ((&alist 'QuickFixes quickfixes))
(omnisharp--choose-and-go-to-quickfix-ido quickfixes other-window))))
(defun omnisharp-navigate-to-solution-file-then-file-member
(&optional other-window)
"Navigates to a file in the solution first, then to a member in that
file. With prefix argument uses another window."
(interactive "P")
(omnisharp-navigate-to-solution-file other-window)
;; Do not set other-window here. No need to use two different
;; windows.
(omnisharp-navigate-to-current-file-member))
(defun omnisharp-navigate-to-solution-file-then-file-member-other-window
(&optional other-window)
(omnisharp-navigate-to-solution-file-then-file-member t))
(defun omnisharp-navigate-to-region
(&optional other-window)
"Navigate to region in current file. If OTHER-WINDOW is given and t,
use another window."
(interactive "P")
(omnisharp--send-command-to-server
"gotoregion"
(omnisharp--get-request-object)
(-lambda ((&alist 'QuickFixes quickfixes))
(omnisharp--choose-and-go-to-quickfix-ido quickfixes other-window))))
(provide 'omnisharp-navigation-actions)