diff --git a/lua/blink/cmp/lib/utils.lua b/lua/blink/cmp/lib/utils.lua index e047b7ccf..329ba3fbb 100644 --- a/lua/blink/cmp/lib/utils.lua +++ b/lua/blink/cmp/lib/utils.lua @@ -105,4 +105,53 @@ function utils.slice(arr, start, finish) return sliced end +--- Generates a random string of length n +--- @param n number +--- @return string +function utils.random_string(n) + n = n or 10 + local str = '' + for _ = 1, n do + str = str .. string.char(math.random(97, 122)) + end + return str +end + +--- Runs a function on an interval until it returns false or the timeout is reached +--- @param fn fun(): false? +--- @param opts { interval_ms: number, timeout_ms: number } +--- @return fun() Cancels the timer +function utils.run_on_interval(fn, opts) + local start_time = vim.uv.now() + local timer = vim.uv.new_timer() + + local function check() + -- Check if we've exceeded the timeout + if (vim.uv.now() - start_time) >= opts.timeout_ms then + timer:stop() + timer:close() + return + end + + -- Run the function and check its result + local result = fn() + if result == false then + timer:stop() + timer:close() + return + end + end + + -- Run immediately first + check() + + -- Then set up the interval + timer:start(0, opts.interval_ms, vim.schedule_wrap(check)) + + return function() + timer:stop() + timer:close() + end +end + return utils diff --git a/lua/blink/cmp/sources/lib/init.lua b/lua/blink/cmp/sources/lib/init.lua index fa9bc0d3a..ca5a2a663 100644 --- a/lua/blink/cmp/sources/lib/init.lua +++ b/lua/blink/cmp/sources/lib/init.lua @@ -168,7 +168,9 @@ function sources.resolve(item) break end end - if item_source == nil then return async.task.new(function(resolve) resolve(item) end) end + if item_source == nil then + return async.task.new(function(resolve) resolve(item) end) + end return item_source:resolve(item):catch(function(err) vim.print('failed to resolve item with error: ' .. err) end) end @@ -183,7 +185,9 @@ function sources.execute(context, item) break end end - if item_source == nil then return async.task.new(function(resolve) resolve() end) end + if item_source == nil then + return async.task.new(function(resolve) resolve() end) + end return item_source :execute(context, item) @@ -247,6 +251,9 @@ end function sources.get_lsp_capabilities(override, include_nvim_defaults) return vim.tbl_deep_extend('force', include_nvim_defaults and vim.lsp.protocol.make_client_capabilities() or {}, { + window = { + workDoneProgress = true, + }, textDocument = { completion = { completionItem = { diff --git a/lua/blink/cmp/sources/lsp.lua b/lua/blink/cmp/sources/lsp.lua index 0fcd4ae94..6b030e63e 100644 --- a/lua/blink/cmp/sources/lsp.lua +++ b/lua/blink/cmp/sources/lsp.lua @@ -1,3 +1,4 @@ +local utils = require('blink.cmp.lib.utils') local known_defaults = { 'commitCharacters', 'editRange', @@ -79,6 +80,7 @@ function lsp:get_completions(context, callback) if context.trigger.kind == CompletionTriggerKind.TriggerCharacter then params.context.triggerCharacter = context.trigger.character end + params.partialResultToken = utils.random_string(32) local _, request_id = client.request('textDocument/completion', params, function(err, result) if err or result == nil then return end @@ -106,6 +108,18 @@ function lsp:get_completions(context, callback) is_incomplete_backward = true, items = items, }) + + vim.print(client.server_capabilities.completionProvider.workDoneProgress) + if client.server_capabilities.completionProvider.workDoneProgress then + utils.run_on_interval(function() + local latest_progress = client.progress:peek() + if latest_progress ~= nil then vim.print(latest_progress) end + if latest_progress == nil or latest_progress.params.token ~= params.partialResultToken then return end + + client.progress:pop() + vim.print('got progress') + end, { interval_ms = 5, timeout_ms = 15000 }) + end end) if request_id ~= nil then cancel_fns[#cancel_fns + 1] = function() client.cancel_request(request_id) end end end @@ -134,7 +148,9 @@ function lsp:resolve(item, callback) callback(resolved_item) end) if not success then callback(item) end - if request_id ~= nil then return function() client.cancel_request(request_id) end end + if request_id ~= nil then + return function() client.cancel_request(request_id) end + end end --- Signature help ---