summaryrefslogtreecommitdiff
path: root/lua/blink/cmp/sources/buffer.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/blink/cmp/sources/buffer.lua')
-rw-r--r--lua/blink/cmp/sources/buffer.lua118
1 files changed, 118 insertions, 0 deletions
diff --git a/lua/blink/cmp/sources/buffer.lua b/lua/blink/cmp/sources/buffer.lua
new file mode 100644
index 0000000..bb679f2
--- /dev/null
+++ b/lua/blink/cmp/sources/buffer.lua
@@ -0,0 +1,118 @@
+-- todo: nvim-cmp only updates the lines that got changed which is better
+-- but this is *speeeeeed* and simple. should add the better way
+-- but ensure it doesn't add too much complexity
+
+local uv = vim.uv
+
+--- @param bufnr integer
+--- @return string
+local function get_buf_text(bufnr)
+ local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
+
+ if bufnr ~= vim.api.nvim_get_current_buf() then return table.concat(lines, '\n') end
+
+ -- exclude word under the cursor for the current buffer
+ local line_number = vim.api.nvim_win_get_cursor(0)[1]
+ local column = vim.api.nvim_win_get_cursor(0)[2]
+ local line = lines[line_number]
+ local start_col = column
+ while start_col > 1 do
+ local char = line:sub(start_col, start_col)
+ if char:match('[%w_\\-]') == nil then break end
+ start_col = start_col - 1
+ end
+ local end_col = column
+ while end_col < #line do
+ local char = line:sub(end_col + 1, end_col + 1)
+ if char:match('[%w_\\-]') == nil then break end
+ end_col = end_col + 1
+ end
+ lines[line_number] = line:sub(1, start_col) .. ' ' .. line:sub(end_col + 1)
+
+ return table.concat(lines, '\n')
+end
+
+local function words_to_items(words)
+ local items = {}
+ for _, word in ipairs(words) do
+ table.insert(items, {
+ label = word,
+ kind = require('blink.cmp.types').CompletionItemKind.Text,
+ insertTextFormat = vim.lsp.protocol.InsertTextFormat.PlainText,
+ insertText = word,
+ })
+ end
+ return items
+end
+
+--- @param buf_text string
+--- @param callback fun(items: blink.cmp.CompletionItem[])
+local function run_sync(buf_text, callback) callback(words_to_items(require('blink.cmp.fuzzy').get_words(buf_text))) end
+
+local function run_async(buf_text, callback)
+ local worker = uv.new_work(
+ -- must use ffi directly since the normal one requires the config which isnt present
+ function(items, cpath)
+ package.cpath = cpath
+ return table.concat(require('blink.cmp.fuzzy.rust').get_words(items), '\n')
+ end,
+ function(words)
+ local items = words_to_items(vim.split(words, '\n'))
+ vim.schedule(function() callback(items) end)
+ end
+ )
+ worker:queue(buf_text, package.cpath)
+end
+
+--- @class blink.cmp.BufferOpts
+--- @field get_bufnrs fun(): integer[]
+
+--- Public API
+
+local buffer = {}
+
+function buffer.new(opts)
+ --- @cast opts blink.cmp.BufferOpts
+
+ local self = setmetatable({}, { __index = buffer })
+ self.get_bufnrs = opts.get_bufnrs
+ or function()
+ return vim
+ .iter(vim.api.nvim_list_wins())
+ :map(function(win) return vim.api.nvim_win_get_buf(win) end)
+ :filter(function(buf) return vim.bo[buf].buftype ~= 'nofile' end)
+ :totable()
+ end
+ return self
+end
+
+function buffer:get_completions(_, callback)
+ local transformed_callback = function(items)
+ callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = items })
+ end
+
+ vim.schedule(function()
+ local bufnrs = require('blink.cmp.lib.utils').deduplicate(self.get_bufnrs())
+ local buf_texts = {}
+ for _, buf in ipairs(bufnrs) do
+ table.insert(buf_texts, get_buf_text(buf))
+ end
+ local buf_text = table.concat(buf_texts, '\n')
+
+ -- should take less than 2ms
+ if #buf_text < 20000 then
+ run_sync(buf_text, transformed_callback)
+ -- should take less than 10ms
+ elseif #buf_text < 500000 then
+ run_async(buf_text, transformed_callback)
+ -- too big so ignore
+ else
+ transformed_callback({})
+ end
+ end)
+
+ -- TODO: cancel run_async
+ return function() end
+end
+
+return buffer