From fc2ce13fcc6454fa12ee1015f921611cd2edc4fa Mon Sep 17 00:00:00 2001 From: ThePrimeAgain Date: Thu, 15 Jan 2026 11:22:01 -0700 Subject: autocomplete appearing to work --- lua/99/agents/helpers.lua | 21 ---------- lua/99/agents/init.lua | 30 -------------- lua/99/extensions/agents/helpers.lua | 21 ++++++++++ lua/99/extensions/agents/init.lua | 45 +++++++++++++++++++++ lua/99/extensions/cmp.lua | 76 ++++++++++++++++++++++-------------- lua/99/extensions/init.lua | 38 ++++++++++++++++-- lua/99/init.lua | 75 ++++++++++++++++++++++------------- lua/99/window/init.lua | 19 ++++++--- 8 files changed, 208 insertions(+), 117 deletions(-) delete mode 100644 lua/99/agents/helpers.lua delete mode 100644 lua/99/agents/init.lua create mode 100644 lua/99/extensions/agents/helpers.lua create mode 100644 lua/99/extensions/agents/init.lua (limited to 'lua') diff --git a/lua/99/agents/helpers.lua b/lua/99/agents/helpers.lua deleted file mode 100644 index a5000cb..0000000 --- a/lua/99/agents/helpers.lua +++ /dev/null @@ -1,21 +0,0 @@ -local M = {} - ---- @param dir string ---- @return _99.Agents.Rule[] -function M.ls(dir) - local cursor_rules_dir = vim.uv.cwd() .. dir - local files = vim.fn.glob(cursor_rules_dir .. "/*.{mdc,md}", false, true) - local rules = {} - - for _, file in ipairs(files) do - local filename = vim.fn.fnamemodify(file, ":t:r") - table.insert(rules, { - name = filename, - path = file, - }) - end - - return rules -end - -return M diff --git a/lua/99/agents/init.lua b/lua/99/agents/init.lua deleted file mode 100644 index be43727..0000000 --- a/lua/99/agents/init.lua +++ /dev/null @@ -1,30 +0,0 @@ -local helpers = require("99.agents.helpers") -local M = {} - ---- @class _99.Agents.Rule ---- @field name string ---- @field path string - ---- @class _99.Agents.Rules ---- @field cursor _99.Agents.Rule[] ---- @field custom _99.Agents.Rule[] - ---- @class _99.Agents.Agent ---- @field rules _99.Agents.Rules - ----@param _99 _99.State ----@return _99.Agents.Rules -function M.rules(_99) - local cursor = helpers.ls(".cursor/rules") - local custom = {} - for _, path in ipairs(_99.custom_rules) do - local c = helpers.ls(path) - table.insert(custom, c) - end - return { - cursor = cursor, - custom = custom, - } -end - -return M diff --git a/lua/99/extensions/agents/helpers.lua b/lua/99/extensions/agents/helpers.lua new file mode 100644 index 0000000..1f4ed01 --- /dev/null +++ b/lua/99/extensions/agents/helpers.lua @@ -0,0 +1,21 @@ +local M = {} + +--- @param dir string +--- @return _99.Agents.Rule[] +function M.ls(dir) + local cwd = vim.fs.joinpath(vim.uv.cwd(), dir) + local files = vim.fn.glob(cwd .. "/*.{mdc,md}", false, true) + local rules = {} + + for _, file in ipairs(files) do + local filename = vim.fn.fnamemodify(file, ":t:r") + table.insert(rules, { + name = filename, + path = file, + }) + end + + return rules +end + +return M diff --git a/lua/99/extensions/agents/init.lua b/lua/99/extensions/agents/init.lua new file mode 100644 index 0000000..f594802 --- /dev/null +++ b/lua/99/extensions/agents/init.lua @@ -0,0 +1,45 @@ +local helpers = require("99.extensions.agents.helpers") +local M = {} + +--- @class _99.Agents.Rule +--- @field name string +--- @field path string + +--- @class _99.Agents.Rules +--- @field cursor _99.Agents.Rule[] +--- @field custom _99.Agents.Rule[] + +--- @class _99.Agents.Agent +--- @field rules _99.Agents.Rules + +---@param _99 _99.State +---@return _99.Agents.Rules +function M.rules(_99) + local cursor = helpers.ls(".cursor/rules") + local custom = {} + for _, path in ipairs(_99.completion.custom_rules or {}) do + local c = helpers.ls(path) + table.insert(custom, c) + end + return { + cursor = cursor, + custom = custom, + } +end + +--- @param rules _99.Agents.Rules +--- @return _99.Agents.Rule[] +function M.rules_to_items(rules) + local items = {} + for _, rule in ipairs(rules.cursor or {}) do + table.insert(items, rule) + end + for _, custom_rules in ipairs(rules.custom or {}) do + for _, rule in ipairs(custom_rules) do + table.insert(items, rule) + end + end + return items +end + +return M diff --git a/lua/99/extensions/cmp.lua b/lua/99/extensions/cmp.lua index e55ae36..30e5d2c 100644 --- a/lua/99/extensions/cmp.lua +++ b/lua/99/extensions/cmp.lua @@ -1,14 +1,24 @@ +local Agents = require("99.extensions.agents") +local SOURCE = "99" + +--- @param _99 _99.State +--- @return _99.Agents.Rule[] +local function rules(_99) + return Agents.rules_to_items(Agents.rules(_99)) +end + --- @class CmpSource --- @field _99 _99.State ---- @field items string[] +--- @field items _99.Agents.Rule[] local CmpSource = {} CmpSource.__index = CmpSource -local SOURCE = "99" - --- @param _99 _99.State function CmpSource.new(_99) - return setmetatable({}, CmpSource) + return setmetatable({ + _99 = _99, + items = rules(_99), + }, CmpSource) end function CmpSource:is_available() @@ -39,41 +49,39 @@ end -- true: I might return more if user types more -- false: this result set is complete function CmpSource:complete(params, callback) - local cmp = require("cmp") local before = params.context.cursor_before_line or "" - local prefix = before:match("(%w+)$") or "" - local items = {} --[[ @as CompletionItem[] ]] - print("complete: context", vim.inspect(params.context)) + if #before > 1 and before:sub(#before - 1) ~= " @" then + callback({ + items = {}, + isIncomplete = false, + }) + return + end + + for _, item in ipairs(self.items) do + table.insert(items, { + label = item.name, + insertText = item.path, + filterText = item.name, + kind = 17, -- file + -- documentation = "here is the documentation and everything associated with it", + -- detail = "detail: right side hint", + }) + end callback({ items = items, - isIncomplete = true, + isIncomplete = false, }) end --- resolve(completion_item, callback) (optional) --- Some sources return lightweight items first, then fill in heavy fields --- only when the user selects an item. --- --- For example: --- - fetch docs lazily --- - compute expensive detail text --- --- If you don’t need it, omit it. +--- TODO: Look into what this could be function CmpSource:resolve(completion_item, callback) - -- You can modify completion_item here. callback(completion_item) end --- execute(completion_item, callback) (optional) --- Called when the item is confirmed, if the item contains an "command" field --- or if your source wants to perform side-effects. --- --- Examples: --- - insert import statements (usually via additionalTextEdits instead) --- - open a snippet, run something, etc. function CmpSource:execute(completion_item, callback) callback(completion_item) end @@ -86,24 +94,32 @@ local function init_for_buffer(_99) local cmp = require("cmp") cmp.setup.buffer({ sources = { - { name = "my_source" }, + { name = SOURCE }, }, }) end --- @param _99 _99.State local function init(_99) - assert(source == nil, "the source must be nil when calling init on an completer") + assert( + source == nil, + "the source must be nil when calling init on an completer" + ) local cmp = require("cmp") source = CmpSource.new(_99) cmp.register_source(SOURCE, source) end -local function refresh_state(_99) end +--- @param _99 _99.State +local function refresh_state(_99) + source.items = rules(_99) +end -return { +--- @type _99.Extensions.Source +local source_wrapper = { init_for_buffer = init_for_buffer, init = init, refresh_state = refresh_state, } +return source_wrapper diff --git a/lua/99/extensions/init.lua b/lua/99/extensions/init.lua index 94190d6..ceb63a5 100644 --- a/lua/99/extensions/init.lua +++ b/lua/99/extensions/init.lua @@ -1,5 +1,37 @@ -local M = {} +local cmp = require("99.extensions.cmp") ---- @param _99 _99.State -function M.init_buffer(_99) +--- @class _99.Extensions.Source +--- @field init_for_buffer fun(_99: _99.State): nil +--- @field init fun(_99: _99.State): nil +--- @field refresh_state fun(_99: _99.State): nil + +--- @param completion _99.Completion | nil +--- @return _99.Extensions.Source | nil +local function get_source(completion) + if not completion or not completion.source then + return + end + local source = completion.source + if source == "cmp" then + return cmp + end end + +return { + --- @param _99 _99.State + init = function(_99) + local source = get_source(_99.completion) + if not source then + return + end + source.init(_99) + end, + + setup_buffer = function(_99) + local source = get_source(_99.completion) + if not source then + return + end + source.init_for_buffer(_99) + end, +} diff --git a/lua/99/init.lua b/lua/99/init.lua index 3faa6e2..a677d6b 100644 --- a/lua/99/init.lua +++ b/lua/99/init.lua @@ -6,6 +6,7 @@ local Window = require("99.window") local get_id = require("99.id") local RequestContext = require("99.request-context") local Range = require("99.geo").Range +local Extensions = require("99.extensions") --- @alias _99.Cleanup fun(): nil @@ -34,6 +35,10 @@ local function create_99_state() } end +--- @class _99.Completion +--- @field source "cmp" | nil +--- @field custom_rules string[] | nil + --- @class _99.Options --- @field logger _99.Logger.Options? --- @field model string? @@ -41,14 +46,14 @@ end --- @field provider _99.Provider? --- @field debug_log_prefix string? --- @field display_errors? boolean ---- @field custom_rules? string[] +--- @field completion _99.Completion? --- unanswered question -- will i need to queue messages one at a time or --- just send them all... So to prepare ill be sending around this state object --- @class _99.State +--- @field completion _99.Completion --- @field model string --- @field md_files string[] ---- @field custom_rules string[] --- @field prompts _99.Prompts --- @field ai_stdout_rows number --- @field languages string[] @@ -146,19 +151,25 @@ end function _99.fill_in_function_prompt() local context = get_context("fill-in-function-with-prompt") + context.logger:debug("start") - Window.capture_input(function(success, response) - context.logger:debug( - "capture_prompt", - "success", - success, - "response", - response - ) - if success then - ops.fill_in_function(context, response) - end - end, {}) + Window.capture_input({ + cb = function(success, response) + context.logger:debug( + "capture_prompt", + "success", + success, + "response", + response + ) + if success then + ops.fill_in_function(context, response) + end + end, + on_load = function() + Extensions.setup_buffer(_99_state) + end, + }) end function _99.fill_in_function() @@ -168,18 +179,23 @@ end function _99.visual_prompt() local context = get_context("over-range-with-prompt") context.logger:debug("start") - Window.capture_input(function(success, response) - context.logger:debug( - "capture_prompt", - "success", - success, - "response", - response - ) - if success then - _99.visual(response) - end - end, {}) + Window.capture_input({ + cb = function(success, response) + context.logger:debug( + "capture_prompt", + "success", + success, + "response", + response + ) + if success then + _99.visual(response) + end + end, + on_load = function() + Extensions.setup_buffer(_99_state) + end, + }) end --- @param prompt string? @@ -250,7 +266,11 @@ function _99.setup(opts) opts = opts or {} _99_state = _99_State.new() _99_state.provider_override = opts.provider - _99_state.custom_rules = opts.custom_rules or {} + _99_state.completion = opts.completion + or { + source = nil, + custom_rules = {}, + } vim.api.nvim_create_autocmd("VimLeavePre", { callback = function() @@ -275,6 +295,7 @@ function _99.setup(opts) _99_state.display_errors = opts.display_errors or false Languages.initialize(_99_state) + Extensions.init(_99_state) end --- @param md string diff --git a/lua/99/window/init.lua b/lua/99/window/init.lua index 8f45433..3475d5a 100644 --- a/lua/99/window/init.lua +++ b/lua/99/window/init.lua @@ -257,9 +257,12 @@ local function set_defaul_win_options(win, name) vim.bo[win.buf_id].swapfile = false end ---- @param cb fun(success: boolean, result: string): nil ---- @param opts {} -function M.capture_input(cb, opts) +--- @class _99.window.CaptureInputOpts +--- @field cb fun(success: boolean, result: string): nil +--- @field on_load? fun(): nil + +--- @param opts _99.window.CaptureInputOpts +function M.capture_input(opts) _ = opts M.clear_active_popups() @@ -299,7 +302,7 @@ function M.capture_input(cb, opts) local lines = vim.api.nvim_buf_get_lines(win.buf_id, 0, -1, false) local result = table.concat(lines, "\n") M.clear_active_popups() - cb(true, result) + opts.cb(true, result) end, }) @@ -322,14 +325,18 @@ function M.capture_input(cb, opts) return end M.clear_active_popups() - cb(false, "") + opts.cb(false, "") end, }) vim.keymap.set("n", "q", function() M.clear_active_popups() - cb(false, "") + opts.cb(false, "") end, { buffer = win.buf_id, nowait = true }) + + if opts.on_load then + vim.schedule(opts.on_load) + end end function M.clear_active_popups() -- cgit v1.3-3-g829e