summaryrefslogtreecommitdiff
path: root/lua/blink/cmp/completion/brackets/semantic.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/blink/cmp/completion/brackets/semantic.lua')
-rw-r--r--lua/blink/cmp/completion/brackets/semantic.lua109
1 files changed, 109 insertions, 0 deletions
diff --git a/lua/blink/cmp/completion/brackets/semantic.lua b/lua/blink/cmp/completion/brackets/semantic.lua
new file mode 100644
index 0000000..c64afbd
--- /dev/null
+++ b/lua/blink/cmp/completion/brackets/semantic.lua
@@ -0,0 +1,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