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
|
local config = require('blink.cmp.config').completion.accept.auto_brackets
local utils = require('blink.cmp.completion.brackets.utils')
local semantic = {}
--- Asynchronously use semantic tokens to determine if brackets should be added
--- @param filetype string
--- @param item blink.cmp.CompletionItem
--- @param callback fun()
function semantic.add_brackets_via_semantic_token(filetype, item, callback)
if not utils.should_run_resolution(filetype, 'semantic_token') then return callback() end
local text_edit = item.textEdit
assert(text_edit ~= nil, 'Got nil text edit while adding brackets via semantic tokens')
local client = vim.lsp.get_client_by_id(item.client_id)
if client == nil then return callback() end
local capabilities = client.server_capabilities.semanticTokensProvider
if not capabilities or not capabilities.legend or (not capabilities.range and not capabilities.full) then
return callback()
end
local token_types = client.server_capabilities.semanticTokensProvider.legend.tokenTypes
local params = {
textDocument = vim.lsp.util.make_text_document_params(),
range = capabilities.range and {
start = { line = text_edit.range.start.line, character = text_edit.range.start.character },
['end'] = { line = text_edit.range.start.line + 1, character = 0 },
} or nil,
}
local cursor_before_call = vim.api.nvim_win_get_cursor(0)
local start_time = vim.uv.hrtime()
client.request(
capabilities.range and 'textDocument/semanticTokens/range' or 'textDocument/semanticTokens/full',
params,
function(err, result)
if err ~= nil or result == nil or #result.data == 0 then return callback() end
-- cancel if it's been too long, or if the cursor moved
local ms_since_call = (vim.uv.hrtime() - start_time) / 1000000
local cursor_after_call = vim.api.nvim_win_get_cursor(0)
if
ms_since_call > config.semantic_token_resolution.timeout_ms
or cursor_before_call[1] ~= cursor_after_call[1]
or cursor_before_call[2] ~= cursor_after_call[2]
then
return callback()
end
for _, token in ipairs(semantic.process_semantic_token_data(result.data, token_types)) do
if
cursor_after_call[1] == token.line
and cursor_after_call[2] >= token.start_col
and cursor_after_call[2] <= token.end_col
and (token.type == 'function' or token.type == 'method')
then
-- add the brackets
local brackets_for_filetype = utils.get_for_filetype(filetype, item)
local line = vim.api.nvim_get_current_line()
local start_col = text_edit.range.start.character + #text_edit.newText
local new_line = line:sub(1, start_col)
.. brackets_for_filetype[1]
.. brackets_for_filetype[2]
.. line:sub(start_col + 1)
vim.api.nvim_set_current_line(new_line)
vim.api.nvim_win_set_cursor(0, { cursor_after_call[1], start_col + #brackets_for_filetype[1] })
callback()
return
end
end
callback()
end
)
end
function semantic.process_semantic_token_data(data, token_types)
local tokens = {}
local idx = 0
local token_line = 0
local token_start_col = 0
while (idx + 1) * 5 <= #data do
local delta_token_line = data[idx * 5 + 1]
local delta_token_start_col = data[idx * 5 + 2]
local delta_token_length = data[idx * 5 + 3]
local type = token_types[data[idx * 5 + 4] + 1]
if delta_token_line > 0 then token_start_col = 0 end
token_line = token_line + delta_token_line
token_start_col = token_start_col + delta_token_start_col
table.insert(tokens, {
line = token_line + 1,
start_col = token_start_col,
end_col = token_start_col + delta_token_length,
type = type,
})
token_start_col = token_start_col + delta_token_length
idx = idx + 1
end
return tokens
end
return semantic.add_brackets_via_semantic_token
|