diff options
Diffstat (limited to 'lua/99/extensions/native.lua')
| -rw-r--r-- | lua/99/extensions/native.lua | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/lua/99/extensions/native.lua b/lua/99/extensions/native.lua new file mode 100644 index 0000000..b025410 --- /dev/null +++ b/lua/99/extensions/native.lua @@ -0,0 +1,162 @@ +local Agents = require("99.extensions.agents") +local Files = require("99.extensions.files") +local Completions = require("99.extensions.completions") + +local DEBOUNCE_MS = 100 + +--- @param items CompletionItem[] +--- @return table[] +local function to_native_items(items) + local out = {} + for _, item in ipairs(items) do + local info = "" + if item.documentation then + if type(item.documentation) == "string" then + info = item.documentation + elseif item.documentation.value then + info = item.documentation.value + end + end + table.insert(out, { + word = item.insertText or item.label, + abbr = item.label, + info = info, + icase = 1, + dup = 0, + }) + end + return out +end + +--- @param _99 _99.State +local function register_providers(_99) + Completions.register(Agents.completion_provider(_99)) + Completions.register(Files.completion_provider()) +end + +--- @param buf number +local function setup_completion_autocmd(buf) + local timer = vim.uv.new_timer() + local group = vim.api.nvim_create_augroup( + "99_native_completion_" .. buf, + { clear = true } + ) + + vim.api.nvim_create_autocmd("TextChangedI", { + group = group, + buffer = buf, + callback = function() + timer:stop() + timer:start( + DEBOUNCE_MS, + 0, + vim.schedule_wrap(function() + if vim.fn.mode() ~= "i" then + return + end + + if not vim.api.nvim_buf_is_valid(buf) then + timer:stop() + return + end + + local line = vim.api.nvim_get_current_line() + local col = vim.fn.col(".") + local before = line:sub(1, col - 1) + + local trigger = nil + local start_col = nil + for _, char in ipairs(Completions.get_trigger_characters()) do + local escaped = Completions.escape_pattern(char) + local pattern = escaped .. "%S*$" + local match_start = before:find(pattern) + if match_start then + trigger = char + start_col = match_start + break + end + end + + if not trigger or not start_col then + return + end + + local items = Completions.get_completions(trigger) + if #items == 0 then + return + end + + local native_items = to_native_items(items) + vim.fn.complete(start_col, native_items) + end) + ) + end, + }) + + vim.api.nvim_create_autocmd("BufWipeout", { + group = group, + buffer = buf, + callback = function() + timer:stop() + pcall(vim.api.nvim_del_augroup_by_id, group) + end, + }) +end + +--- @param buf number +local function setup_keymaps(buf) + vim.keymap.set("i", "<Tab>", function() + if vim.fn.pumvisible() == 1 then + return "<C-n>" + end + return "<Tab>" + end, { buffer = buf, expr = true, noremap = true }) + + vim.keymap.set("i", "<S-Tab>", function() + if vim.fn.pumvisible() == 1 then + return "<C-p>" + end + return "<S-Tab>" + end, { buffer = buf, expr = true, noremap = true }) +end + +--- @param _ _99.State +local function init_for_buffer(_) + local buf = vim.api.nvim_get_current_buf() + + vim.bo[buf].filetype = "99prompt" + vim.opt_local.completeopt = "menuone,noinsert,noselect,popup,fuzzy" + + setup_completion_autocmd(buf) + setup_keymaps(buf) +end + +--- @param _99 _99.State +local function init(_99) + local rule_dirs = {} + if _99.completion and _99.completion.custom_rules then + for _, dir in ipairs(_99.completion.custom_rules) do + table.insert(rule_dirs, dir) + end + end + + if _99.completion and _99.completion.files then + Files.setup(_99.completion.files, rule_dirs) + else + Files.setup({ enabled = true }, rule_dirs) + end + + register_providers(_99) +end + +--- @param _99 _99.State +local function refresh_state(_99) + register_providers(_99) +end + +--- @type _99.Extensions.Source +return { + init_for_buffer = init_for_buffer, + init = init, + refresh_state = refresh_state, +} |
