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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
local vim = vim
local validate = vim.validate
local api = vim.api
local vfn = vim.fn
local util = require 'vim.lsp.util'
local list_extend = vim.list_extend
local M = {}
local function ok_or_nil(status, ...)
if not status then return end
return ...
end
local function npcall(fn, ...)
return ok_or_nil(pcall(fn, ...))
end
local function request(method, params, callback)
validate {
method = {method, 's'};
callback = {callback, 'f', true};
}
return vim.lsp.buf_request(0, method, params, callback)
end
--- Sends a notification through all clients associated with current buffer.
--
--@return `true` if server responds.
function M.server_ready()
return not not vim.lsp.buf_notify(0, "window/progress", {})
end
--- Displays hover information about the symbol under the cursor in a floating
--- window. Calling the function twice will jump into the floating window.
function M.hover()
local params = util.make_position_params()
request('textDocument/hover', params)
end
--- Jumps to the declaration of the symbol under the cursor.
---
function M.declaration()
local params = util.make_position_params()
request('textDocument/declaration', params)
end
--- Jumps to the definition of the symbol under the cursor.
---
function M.definition()
local params = util.make_position_params()
request('textDocument/definition', params)
end
--- Jumps to the definition of the type of the symbol under the cursor.
---
function M.type_definition()
local params = util.make_position_params()
request('textDocument/typeDefinition', params)
end
--- Lists all the implementations for the symbol under the cursor in the
--- quickfix window.
function M.implementation()
local params = util.make_position_params()
request('textDocument/implementation', params)
end
--- Displays signature information about the symbol under the cursor in a
--- floating window.
function M.signature_help()
local params = util.make_position_params()
request('textDocument/signatureHelp', params)
end
--- Retrieves the completion items at the current cursor position. Can only be
--- called in Insert mode.
function M.completion(context)
local params = util.make_position_params()
params.context = context
return request('textDocument/completion', params)
end
--- Formats the current buffer.
---
--- The optional {options} table can be used to specify FormattingOptions, a
--- list of which is available at
--- https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting.
--- Some unspecified options will be automatically derived from the current
--- Neovim options.
function M.formatting(options)
local params = util.make_formatting_params(options)
return request('textDocument/formatting', params)
end
--- Perform |vim.lsp.buf.formatting()| synchronously.
---
--- Useful for running on save, to make sure buffer is formatted prior to being
--- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|.
function M.formatting_sync(options, timeout_ms)
local params = util.make_formatting_params(options)
local result = vim.lsp.buf_request_sync(0, "textDocument/formatting", params, timeout_ms)
if not result then return end
result = result[1].result
vim.lsp.util.apply_text_edits(result)
end
function M.range_formatting(options, start_pos, end_pos)
validate {
options = {options, 't', true};
start_pos = {start_pos, 't', true};
end_pos = {end_pos, 't', true};
}
local sts = vim.bo.softtabstop;
options = vim.tbl_extend('keep', options or {}, {
tabSize = (sts > 0 and sts) or (sts < 0 and vim.bo.shiftwidth) or vim.bo.tabstop;
insertSpaces = vim.bo.expandtab;
})
local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<'))
local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>'))
-- convert to 0-index
A[1] = A[1] - 1
B[1] = B[1] - 1
-- account for encoding.
if A[2] > 0 then
A = {A[1], util.character_offset(0, A[1], A[2])}
end
if B[2] > 0 then
B = {B[1], util.character_offset(0, B[1], B[2])}
end
local params = {
textDocument = { uri = vim.uri_from_bufnr(0) };
range = {
start = { line = A[1]; character = A[2]; };
["end"] = { line = B[1]; character = B[2]; };
};
options = options;
}
return request('textDocument/rangeFormatting', params)
end
--- Renames all references to the symbol under the cursor. If {new_name} is not
--- provided, the user will be prompted for a new name using |input()|.
function M.rename(new_name)
-- TODO(ashkan) use prepareRename
-- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position.
local params = util.make_position_params()
new_name = new_name or npcall(vfn.input, "New Name: ", vfn.expand('<cword>'))
if not (new_name and #new_name > 0) then return end
params.newName = new_name
request('textDocument/rename', params)
end
--- Lists all the references to the symbol under the cursor in the quickfix window.
---
function M.references(context)
validate { context = { context, 't', true } }
local params = util.make_position_params()
params.context = context or {
includeDeclaration = true;
}
params[vim.type_idx] = vim.types.dictionary
request('textDocument/references', params)
end
--- Lists all symbols in the current buffer in the quickfix window.
---
function M.document_symbol()
local params = { textDocument = util.make_text_document_params() }
request('textDocument/documentSymbol', params)
end
local function pick_call_hierarchy_item(call_hierarchy_items)
if not call_hierarchy_items then return end
if #call_hierarchy_items == 1 then
return call_hierarchy_items[1]
end
local items = {}
for i, item in ipairs(call_hierarchy_items) do
local entry = item.detail or item.name
table.insert(items, string.format("%d. %s", i, entry))
end
local choice = vim.fn.inputlist(items)
if choice < 1 or choice > #items then
return
end
return choice
end
function M.incoming_calls()
local params = util.make_position_params()
request('textDocument/prepareCallHierarchy', params, function(_, _, result)
local call_hierarchy_item = pick_call_hierarchy_item(result)
vim.lsp.buf_request(0, 'callHierarchy/incomingCalls', { item = call_hierarchy_item })
end)
end
function M.outgoing_calls()
local params = util.make_position_params()
request('textDocument/prepareCallHierarchy', params, function(_, _, result)
local call_hierarchy_item = pick_call_hierarchy_item(result)
vim.lsp.buf_request(0, 'callHierarchy/outgoingCalls', { item = call_hierarchy_item })
end)
end
--- Lists all symbols in the current workspace in the quickfix window.
---
--- The list is filtered against the optional argument {query};
--- if the argument is omitted from the call, the user is prompted to enter a string on the command line.
--- An empty string means no filtering is done.
function M.workspace_symbol(query)
query = query or npcall(vfn.input, "Query: ")
local params = {query = query}
request('workspace/symbol', params)
end
--- Send request to server to resolve document highlights for the
--- current text document position. This request can be associated
--- to key mapping or to events such as `CursorHold`, eg:
---
--- <pre>
--- vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]]
--- vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]]
--- vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]]
--- </pre>
function M.document_highlight()
local params = util.make_position_params()
request('textDocument/documentHighlight', params)
end
function M.clear_references()
util.buf_clear_references()
end
function M.code_action(context)
validate { context = { context, 't', true } }
context = context or { diagnostics = util.get_line_diagnostics() }
local params = util.make_range_params()
params.context = context
request('textDocument/codeAction', params)
end
function M.execute_command(command)
validate {
command = { command.command, 's' },
arguments = { command.arguments, 't', true }
}
request('workspace/executeCommand', command)
end
return M
-- vim:sw=2 ts=2 et
|