-- luacheck: globals describe it assert before_each ---@diagnostic disable: undefined-field, missing-fields local Completions = require("99.extensions.completions") local eq = assert.are.same local function mock_provider(trigger, name, valid_tokens) return { trigger = trigger, name = name, get_items = function() local items = {} for token, _ in pairs(valid_tokens) do table.insert(items, { label = token, insertText = trigger .. token, filterText = trigger .. token, kind = 1, }) end return items end, is_valid = function(token) return valid_tokens[token] ~= nil end, resolve = function(token) return valid_tokens[token] end, } end describe("completions", function() before_each(function() Completions._reset() end) it("register and get_trigger_characters", function() Completions.register(mock_provider("#", "rules", {})) Completions.register(mock_provider("@", "files", {})) eq({ "#", "@" }, Completions.get_trigger_characters()) end) it("register replaces provider with same trigger", function() Completions.register( mock_provider("#", "rules-v1", { old = "old-content" }) ) Completions.register( mock_provider("#", "rules-v2", { new = "new-content" }) ) local triggers = Completions.get_trigger_characters() eq({ "#" }, triggers) local refs = Completions.parse("use #new in prompt") eq(1, #refs) eq("new-content", refs[1].content) local old_refs = Completions.parse("use #old in prompt") eq(0, #old_refs) end) it("get_keyword_pattern builds pattern from triggers", function() Completions.register(mock_provider("#", "rules", {})) Completions.register(mock_provider("@", "files", {})) eq("[#@]\\k*", Completions.get_keyword_pattern()) end) it("get_completions returns items for known trigger", function() Completions.register(mock_provider("#", "rules", { debug = "content" })) local items = Completions.get_completions("#") eq(1, #items) eq("debug", items[1].label) eq("#debug", items[1].insertText) end) it("get_completions returns empty for unknown trigger", function() Completions.register(mock_provider("#", "rules", {})) eq({}, Completions.get_completions("@")) end) it("parse extracts valid tokens and resolves content", function() Completions.register(mock_provider("#", "rules", { ["debug.md"] = "content", })) Completions.register(mock_provider("@", "files", { ["utils.lua"] = "```lua\ncode\n```", })) local refs = Completions.parse("add logging #debug.md and read @utils.lua") eq(2, #refs) eq("content", refs[1].content) eq("```lua\ncode\n```", refs[2].content) end) it("parse skips invalid tokens", function() Completions.register(mock_provider("#", "rules", { ["valid.md"] = "resolved", })) local refs = Completions.parse("#valid.md #nonexistent") eq(1, #refs) eq("resolved", refs[1].content) end) it("parse returns empty for no tokens", function() Completions.register(mock_provider("#", "rules", { a = "b" })) eq({}, Completions.parse("just a plain prompt")) end) it("real providers register and resolve through the registry", function() local Agents = require("99.extensions.agents") local Files = require("99.extensions.files") -- Set up files module local default_exclude = { ".env", ".env.*", "node_modules", ".git", "dist", "build", "*.log", ".DS_Store", "tmp", ".cursor", } Files.setup({ enabled = true, exclude = default_exclude }, {}) Files.set_project_root(vim.uv.cwd() or "") Files.discover_files() -- Build a minimal state local state = { rules = Agents.rules({ completion = { cursor_rules = "scratch/cursor/rules/", custom_rules = {}, }, }), } -- Register real providers through the registry Completions.register(Agents.completion_provider(state)) Completions.register(Files.completion_provider()) -- Verify triggers registered local triggers = Completions.get_trigger_characters() eq(2, #triggers) -- Parse a prompt with a real @file reference local refs = Completions.parse("check @scratch/refresh.lua") assert.is_true(#refs > 0, "expected at least one resolved reference") -- Verify resolved content is a real code fence with non-empty body assert.is_true( refs[1].content:sub(1, 6) == "```lua", "expected code fence from real file provider" ) assert.is_true(#refs[1].content > 20, "expected non-trivial file content") -- Clean up Files.set_project_root("") end) end)