Skip to content

Commit

Permalink
feat: rework sources again
Browse files Browse the repository at this point in the history
  • Loading branch information
Saghen committed Sep 27, 2024
1 parent 37dbee4 commit 7568de9
Show file tree
Hide file tree
Showing 12 changed files with 355 additions and 219 deletions.
2 changes: 2 additions & 0 deletions lua/blink/cmp/accept.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
local utils = {}

-- todo: doesnt work when the item contains characters not included in the context regex

local function accept(item)
local sources = require('blink.cmp.sources.lib')
local fuzzy = require('blink.cmp.fuzzy')
Expand Down
7 changes: 3 additions & 4 deletions lua/blink/cmp/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
--- @field blocked_trigger_characters string[]

--- @class blink.cmp.SourceConfig
--- @field providers blink.cmp.SourceProviderConfig[]
--- @field providers blink.cmp.SourceProviderConfig[][]
---
--- @class blink.cmp.SourceProviderConfig
--- @field [1] string
Expand Down Expand Up @@ -101,9 +101,8 @@ local config = {
},
sources = {
providers = {
{ 'blink.cmp.sources.lsp' },
{ 'blink.cmp.sources.buffer', score_offset = -9 },
{ 'blink.cmp.sources.snippets', score_offset = -3 },
{ { 'blink.cmp.sources.lsp' }, { 'blink.cmp.sources.snippets', score_offset = -3 } },
{ { 'blink.cmp.sources.buffer', score_offset = -9 } },
},
},
windows = {
Expand Down
2 changes: 1 addition & 1 deletion lua/blink/cmp/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ cmp.setup = function(opts)
cmp.fuzzy = require('blink.cmp.fuzzy')
cmp.fuzzy.init_db(vim.fn.stdpath('data') .. '/blink/cmp/fuzzy.db')

cmp.trigger.listen_on_show(function(context) cmp.sources.completions(context) end)
cmp.trigger.listen_on_show(function(context) cmp.sources.request_completions(context) end)
cmp.trigger.listen_on_hide(function()
cmp.sources.cancel_completions()
cmp.windows.autocomplete.close()
Expand Down
3 changes: 1 addition & 2 deletions lua/blink/cmp/sources/buffer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ local function words_to_items(words)
kind = vim.lsp.protocol.CompletionItemKind.Text,
insertTextFormat = vim.lsp.protocol.InsertTextFormat.Snippet,
insertText = word,
score_offset = -9,
})
end
return items
Expand Down Expand Up @@ -69,7 +68,7 @@ function buffer.new(config) return setmetatable(config, { __index = buffer }) en

function buffer:get_completions(_, callback)
local transformed_callback = function(items)
callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = items })
callback({ is_incomplete_forward = false, is_incomplete_backward = false, context = context, items = items })
end

local buf_text = get_buf_text()
Expand Down
201 changes: 137 additions & 64 deletions lua/blink/cmp/sources/lib/async.lua
Original file line number Diff line number Diff line change
@@ -1,109 +1,182 @@
--- Allows chaining of async operations without callback hell
---
--- @class blink.cmp.Task
--- @field status 1 | 2 | 3
--- @field result any
--- @field new fun(fn: fun(..., cb: fun(result: any, success: boolean | nil)), ...): blink.cmp.Task
--- @field await fun(self: blink.cmp.Task, cb: fun(success: boolean, result: any))
--- @field status 1 | 2 | 3 | 4
--- @field result any | nil
--- @field error any | nil
--- @field new fun(fn: fun(resolve: fun(result: any), reject: fun(err: any))): blink.cmp.Task
---
--- @field cancel fun(self: blink.cmp.Task)
--- @field map fun(self: blink.cmp.Task, fn: fun(result: any): blink.cmp.Task | any)
--- @field catch fun(self: blink.cmp.Task, fn: fun(err: any): blink.cmp.Task | any)
---
--- @field on_completion fun(self: blink.cmp.Task, cb: fun(result: any))
--- @field on_failure fun(self: blink.cmp.Task, cb: fun(err: any))
--- @field on_cancel fun(self: blink.cmp.Task, cb: fun())

local STATUS = {
RUNNING = 1,
COMPLETED = 2,
FAILED = 3,
CANCELLED = 4,
}

local task = {}
local task = {
__task = true,
}

function task.new(fn, ...)
function task.new(fn)
local self = setmetatable({}, { __index = task })
self.status = STATUS.RUNNING
self._awaits = {}
self._completion_cbs = {}
self._failure_cbs = {}
self._cancel_cbs = {}
self.result = nil
self.error = nil

local arg = { ... }

local success, cancel_or_err = pcall(function()
local cb = function(result, success)
if self.status ~= STATUS.RUNNING then return end
if success == false then return self:cancel() end
local resolve = function(result)
if self.status ~= STATUS.RUNNING then return end

self.status = STATUS.COMPLETED
self.result = result
self.status = STATUS.COMPLETED
self.result = result

for _, await_cb in ipairs(self._awaits) do
await_cb(true, result)
end
for _, cb in ipairs(self._completion_cbs) do
cb(result)
end
end

local reject = function(err)
if self.status ~= STATUS.RUNNING then return end

-- todo: why doesnt unpack work?
if #arg == 0 then
return fn(cb)
elseif #arg == 1 then
return fn(arg[1], cb)
elseif #arg == 2 then
return fn(arg[1], arg[2], cb)
self.status = STATUS.FAILED
self.error = err

for _, cb in ipairs(self._failure_cbs) do
cb(err)
end
end

return fn(unpack(arg), cb)
end)
local success, cancel_fn_or_err = pcall(function() return fn(resolve, reject) end)

if not success then
vim.print('Failed to create task :' .. cancel_or_err)
self:cancel()
elseif type(cancel_or_err) ~= 'function' then
vim.print('Cancel is not a function')
vim.print(cancel_or_err)
self:cancel()
else
self._cancel = cancel_or_err
reject(cancel_fn_or_err)
elseif type(cancel_fn_or_err) == 'function' then
self._cancel = cancel_fn_or_err
end

return self
end

function task:cancel()
if self.status ~= STATUS.RUNNING then return end
self.status = STATUS.FAILED
self.status = STATUS.CANCELLED

if self._cancel ~= nil then self._cancel() end
for _, cb in ipairs(self._cancel_cbs) do
cb()
end
end

--- mappings

function task:map(fn)
local chained_task
chained_task = task.new(function(resolve, reject)
self:on_completion(function(result)
local mapped_result = fn(result)
if type(mapped_result) == 'table' and mapped_result.__task then
mapped_result:on_completion(resolve)
mapped_result:on_failure(reject)
mapped_result:on_cancel(function() chained_task:cancel() end)
return
end
resolve(mapped_result)
end)
self:on_failure(reject)
self:on_cancel(function() chained_task:cancel() end)
return function() chained_task:cancel() end
end)
return chained_task
end

function task:catch(fn)
local chained_task
chained_task = task.new(function(resolve, reject)
self:on_completion(resolve)
self:on_failure(function(err)
local mapped_err = fn(err)
if type(mapped_err) == 'table' and mapped_err.is_task then
mapped_err:on_completion(resolve)
mapped_err:on_failure(reject)
mapped_err:on_cancel(function() chained_task:cancel() end)
return
end
resolve(mapped_err)
end)
self:on_cancel(function() chained_task:cancel() end)
return function() chained_task:cancel() end
end)
return chained_task
end

--- events

for _, await_cb in ipairs(self._awaits) do
await_cb(false)
function task:on_completion(cb)
if self.status == STATUS.COMPLETED then
cb(self.result)
elseif self.status == STATUS.RUNNING then
table.insert(self._completion_cbs, cb)
end
return self
end

function task:await(cb)
function task:on_failure(cb)
if self.status == STATUS.FAILED then
cb(false)
elseif self.status == STATUS.COMPLETED then
cb(true, self.result)
else
table.insert(self._awaits, cb)
cb(self.error)
elseif self.status == STATUS.RUNNING then
table.insert(self._failure_cbs, cb)
end
return self
end

function task:map(f)
return task.new(function(cb)
self:await(function(success, result)
if success then
cb(f(result))
else
cb(nil, false)
end
end)
return function() self:cancel() end
end)
function task:on_cancel(cb)
if self.status == STATUS.CANCELLED then
cb()
elseif self.status == STATUS.RUNNING then
table.insert(self._cancel_cbs, cb)
end
return self
end

function task:map_error(f)
return task.new(function(cb)
self:await(function(success, result)
if success then
cb(result)
else
cb(f())
--- utils

function task.await_all(tasks)
return task.new(function(resolve)
local results = {}

local function resolve_if_completed()
-- we can't check #results directly because a table like
-- { [2] = { ... } } has a length of 2
for i = 1, #tasks do
if results[i] == nil then return end
end
end)
return function() self:cancel() end
resolve(results)
end

for idx, task in ipairs(tasks) do
task:on_completion(function(result)
results[idx] = { status = STATUS.COMPLETED, result = result }
resolve_if_completed()
end)
task:on_failure(function(err)
results[idx] = { status = STATUS.FAILED, err = err }
resolve_if_completed()
end)
task:on_cancel(function()
results[idx] = { status = STATUS.CANCELLED }
resolve_if_completed()
end)
end
end)
end

Expand Down
Loading

0 comments on commit 7568de9

Please sign in to comment.