Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Saghen committed Dec 9, 2024
1 parent b8e1e5b commit a5b7fc2
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 23 deletions.
6 changes: 5 additions & 1 deletion lua/blink/cmp/lib/async.lua
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ function task.await_all(tasks)
for _, sub_task in ipairs(tasks) do
sub_task:cancel()
end
all_task:cancel()
if all_task == nil then
vim.schedule(function() all_task:cancel() end)
else
all_task:cancel()
end
end)
end
end)
Expand Down
1 change: 0 additions & 1 deletion lua/blink/cmp/sources/lib/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ function sources.emit_completions(context, _items_by_provider)
for id, items in pairs(_items_by_provider) do
if sources.providers[id]:should_show_items(context, items) then items_by_provider[id] = items end
end
vim.print('emitting')
sources.completions_emitter:emit({ context = context, items = items_by_provider })
end

Expand Down
14 changes: 12 additions & 2 deletions lua/blink/cmp/sources/lib/provider/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ end
function source:get_completions(context, on_items)
-- return the previous successful completions if the context is the same
-- and the data doesn't need to be updated
-- or if the list is async, since we don't want to cause a flash of no items
if self.list ~= nil and self.list:is_valid_for_context(context) then
self.list:set_on_items(on_items)
self.list:emit()
Expand All @@ -77,9 +78,18 @@ function source:get_completions(context, on_items)
and { kind = vim.lsp.protocol.CompletionTriggerKind.TriggerCharacter, character = context.trigger.character }
or { kind = vim.lsp.protocol.CompletionTriggerKind.Invoked }

-- TODO: error handling
local async_initial_items = self.list ~= nil and self.list.items or {}
if self.list ~= nil then self.list:destroy() end
self.list = require('blink.cmp.sources.lib.provider.list').new(self, context, on_items)

self.list = require('blink.cmp.sources.lib.provider.list').new(
self,
context,
on_items,
-- HACK: if the source is async, we're not reusing the previous list and the response was marked as incomplete,
-- the user will see a flash of no items from the provider, since the list emits immediately. So we hack around
-- this for now
{ async_initial_items = async_initial_items }
)
end

function source:should_show_items(context, items)
Expand Down
27 changes: 17 additions & 10 deletions lua/blink/cmp/sources/lib/provider/list.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,34 @@
--- @field context blink.cmp.Context
--- @field items blink.cmp.CompletionItem[]
--- @field on_items fun(items: blink.cmp.CompletionItem[])
--- @field has_completed boolean
--- @field is_incomplete_backward boolean
--- @field is_incomplete_forward boolean
--- @field cancel_completions? fun(): nil
---
--- @field new fun(module: blink.cmp.Source, config: blink.cmp.SourceProviderConfigWrapper, context: blink.cmp.Context, on_response: fun(response: blink.cmp.CompletionResponse)): blink.cmp.SourceProviderList
--- @field new fun(provider: blink.cmp.SourceProvider, config: blink.cmp.SourceProviderConfigWrapper, context: blink.cmp.Context, on_items: fun(items: blink.cmp.CompletionItem[]), opts: blink.cmp.SourceProviderListOpts): blink.cmp.SourceProviderList
--- @field append fun(self: blink.cmp.SourceProviderList, response: blink.cmp.CompletionResponse): nil
--- @field emit fun(self: blink.cmp.SourceProviderList): nil
--- @field destroy fun(self: blink.cmp.SourceProviderList): nil
--- @field set_on_items fun(self: blink.cmp.SourceProviderList, on_response: fun(response: blink.cmp.CompletionResponse)): nil
--- @field is_valid_for_context fun(self: blink.cmp.SourceProviderList, context: blink.cmp.Context): boolean
---
--- @class blink.cmp.SourceProviderListOpts
--- @field async_initial_items blink.cmp.CompletionItem[]

--- @type blink.cmp.SourceProviderList
--- @diagnostic disable-next-line: missing-fields
local list = {}

function list.new(provider, context, on_items)
function list.new(provider, context, on_items, opts)
--- @type blink.cmp.SourceProviderList
local self = setmetatable({
provider = provider,
context = context,
items = {},
items = opts.async_initial_items,
on_items = on_items,

has_completed = false,
is_incomplete_backward = true,
is_incomplete_forward = true,
}, { __index = list })
Expand All @@ -45,9 +50,9 @@ function list.new(provider, context, on_items)
)
end

-- if async, immediately send the default response
-- if async, immediately send the default response/initial items
local is_async = self.provider.config.async(self.context)
if self.provider.config.async(self.context) then self:append(default_response) end
if self.provider.config.async(self.context) then self:emit() end

-- if not async and timeout is set, send the default response after the timeout
local timeout_ms = self.provider.config.timeout_ms(self.context)
Expand All @@ -57,8 +62,12 @@ function list.new(provider, context, on_items)
end

function list:append(response)
self.is_incomplete_backward = response.is_incomplete_backward
self.is_incomplete_forward = response.is_incomplete_forward
if not self.has_completed then
self.has_completed = true
self.is_incomplete_backward = response.is_incomplete_backward
self.is_incomplete_forward = response.is_incomplete_forward
self.items = {}
end

-- add non-lsp metadata
local source_score_offset = self.provider.config.score_offset(self.context) or 0
Expand All @@ -69,6 +78,7 @@ function list:append(response)
item.source_name = self.provider.name
end

-- combine with existing items
local new_items = {}
vim.list_extend(new_items, self.items)
vim.list_extend(new_items, response.items)
Expand All @@ -78,9 +88,6 @@ function list:append(response)
if self.provider.config.transform_items ~= nil then
self.items = self.provider.config.transform_items(self.context, self.items)
end
vim.print(
'appended ' .. #response.items .. ' items for ' .. self.provider.id .. ' for a total of ' .. #self.items .. ' items'
)

self:emit()
end
Expand Down
38 changes: 29 additions & 9 deletions lua/blink/cmp/sources/lib/tree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
--- @field nodes blink.cmp.SourceTreeNode[]
--- @field new fun(context: blink.cmp.Context, all_sources: blink.cmp.SourceProvider[]): blink.cmp.SourceTree
--- @field get_completions fun(self: blink.cmp.SourceTree, context: blink.cmp.Context, on_items_by_provider: fun(items_by_provider: table<string, blink.cmp.CompletionItem[]>)): blink.cmp.Task
--- @field emit_completions fun(self: blink.cmp.SourceTree, items_by_provider: table<string, blink.cmp.CompletionItem[]>, on_items_by_provider: fun(items_by_provider: table<string, blink.cmp.CompletionItem[]>)): nil
--- @field destroy fun(self: blink.cmp.SourceTree)
--- @field get_top_level_nodes fun(self: blink.cmp.SourceTree): blink.cmp.SourceTreeNode[]
--- @field detect_cycle fun(node: blink.cmp.SourceTreeNode, visited?: table<string, boolean>, path?: table<string, boolean>): boolean
Expand Down Expand Up @@ -68,14 +69,10 @@ function tree:get_completions(context, on_items_by_provider)
if not nodes_falling_back[dependency.id] then return async.task.empty() end
end

vim.print('getting completions for ' .. node.id)
return async.task.new(function(resolve, reject)
return node.source:get_completions(context, function(items)
items_by_provider[node.id] = items
if should_push_upstream then
vim.print('async upstreaming')
on_items_by_provider(items_by_provider)
end
if should_push_upstream then self:emit_completions(items_by_provider, on_items_by_provider) end
if #items ~= 0 then return resolve() end

-- run dependents if the source returned 0 items
Expand All @@ -86,20 +83,43 @@ function tree:get_completions(context, on_items_by_provider)
end)
end

vim.print('calling')
-- run the top level nodes and let them fall back to their dependents if needed
local tasks = vim.tbl_map(function(node) return get_completions_for_node(node) end, self:get_top_level_nodes())
return async.task
.await_all(tasks)
:map(function()
vim.print('pushing upstream')
should_push_upstream = true
on_items_by_provider(items_by_provider)
self:emit_completions(items_by_provider, on_items_by_provider)
end)
:catch(function(err) vim.print('failed to get completions with error: ' .. err) end)
end

function tree:emit_completions(context, items_by_provider, on_items_by_provider) end
function tree:emit_completions(items_by_provider, on_items_by_provider)
local nodes_falling_back = {}
local final_items_by_provider = {}

local add_node_items
add_node_items = function(node)
for _, dependency in ipairs(node.dependencies) do
if not nodes_falling_back[dependency.id] then return end
end
local items = items_by_provider[node.id]
if items ~= nil and #items > 0 then
final_items_by_provider[node.id] = items
else
nodes_falling_back[node.id] = true
for _, dependent in ipairs(node.dependents) do
add_node_items(dependent)
end
end
end

for _, node in ipairs(self:get_top_level_nodes()) do
add_node_items(node)
end

on_items_by_provider(final_items_by_provider)
end

function tree:destroy() end

Expand Down

0 comments on commit a5b7fc2

Please sign in to comment.