summaryrefslogtreecommitdiff
path: root/lua/99/init.lua
diff options
context:
space:
mode:
authorBogdan Nikolov <84232456+0xr3ngar@users.noreply.github.com>2026-02-19 20:30:08 +0100
committerGitHub <noreply@github.com>2026-02-19 20:30:08 +0100
commit725bf38ffc08cb8b537cbff322ef2d889b197dad (patch)
tree2fa53c6b40463b8a18d9e175617b9b413a844e70 /lua/99/init.lua
parent6d219f47aa29cafb1ec39c41d4dd0a4875a74506 (diff)
parentd34e4dcbcc1f95d8f31f851baad18da12c59e7df (diff)
downloada4-725bf38ffc08cb8b537cbff322ef2d889b197dad.tar.xz
a4-725bf38ffc08cb8b537cbff322ef2d889b197dad.zip
Merge branch 'master' into master
Diffstat (limited to 'lua/99/init.lua')
-rw-r--r--lua/99/init.lua271
1 files changed, 171 insertions, 100 deletions
diff --git a/lua/99/init.lua b/lua/99/init.lua
index ad16465..a512ab6 100644
--- a/lua/99/init.lua
+++ b/lua/99/init.lua
@@ -43,19 +43,22 @@ end
--- @alias _99.Cleanup fun(): nil
+--- @class _99.RequestEntry.Data.Search
+--- @field type "search"
+--- @field qfix_items _99.Search.Result[]
+
+--- @class _99.RequestEntry.Data.Visual
+--- @field type "visual"
+
+-- luacheck: ignore
+--- @alias _99.RequestEntry.Data _99.RequestEntry.Data.Search | _99.RequestEntry.Data.Tutorial | _99.RequestEntry.Data.Visual
+
--- @class _99.RequestEntry
---- @field id number
---- @field operation string
---- @field status "running" | "success" | "failed" | "cancelled"
---- @field filename string
---- @field lnum number
---- @field col number
+--- @field context _99.RequestContext
+--- @field status _99.Request.State
+--- @field point _99.Point
--- @field started_at number
-
---- @class _99.ActiveRequest
---- @field clean_up _99.Cleanup
---- @field request_id number
---- @field name string
+--- @field operation_data _99.RequestEntry.Data | nil
--- @class _99.StateProps
--- @field model string
@@ -67,10 +70,10 @@ end
--- @field display_errors boolean
--- @field auto_add_skills boolean
--- @field provider_override _99.Providers.BaseProvider?
---- @field __active_requests table<number, _99.ActiveRequest>
--- @field __view_log_idx number
--- @field __request_history _99.RequestEntry[]
--- @field __request_by_id table<number, _99.RequestEntry>
+--- @field tmp_dir string | nil
--- @return _99.StateProps
local function create_99_state()
@@ -84,15 +87,15 @@ local function create_99_state()
display_errors = false,
provider_override = nil,
auto_add_skills = false,
- __active_requests = {},
__view_log_idx = 1,
__request_history = {},
__request_by_id = {},
+ tmp_dir = nil,
}
end
--- @class _99.Completion
---- @field source "cmp" | nil
+--- @field source "cmp" | "blink" | nil
--- @field custom_rules string[]
--- @field files _99.Files.Config?
@@ -106,6 +109,7 @@ end
--- @field display_errors? boolean
--- @field auto_add_skills? boolean
--- @field completion _99.Completion?
+--- @field tmp_dir? string
--- 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
@@ -123,11 +127,11 @@ end
--- @field provider_override _99.Providers.BaseProvider?
--- @field auto_add_skills boolean
--- @field rules _99.Agents.Rules
---- @field __active_requests table<number, _99.ActiveRequest>
--- @field __view_log_idx number
--- @field __request_history _99.RequestEntry[]
--- @field __request_by_id table<number, _99.RequestEntry>
--- @field __active_marks _99.Mark[]
+--- @field tmp_dir string | nil
local _99_State = {}
_99_State.__index = _99_State
@@ -152,49 +156,63 @@ function _99_State:refresh_rules()
Extensions.refresh(self)
end
+--- @param tutorial _99.RequestEntry.Data.Tutorial
+function _99_State:open_tutorial(tutorial) end
+
--- @param context _99.RequestContext
--- @return _99.RequestEntry
function _99_State:track_request(context)
+ assert(
+ context.operation,
+ "must have an operation defined to track the request"
+ )
+
local point = context.range and context.range.start or Point:from_cursor()
local entry = {
- id = context.xid,
- operation = context.operation or "request",
- status = "running",
- filename = context.full_path,
- lnum = point.row,
- col = point.col,
+ context = context,
+ status = "requesting",
+ point = point,
started_at = time.now(),
+ operation_data = nil,
}
table.insert(self.__request_history, entry)
- self.__request_by_id[entry.id] = entry
+ self.__request_by_id[context.xid] = entry
return entry
end
---- @param id number
---- @param status "success" | "failed" | "cancelled"
-function _99_State:finish_request(id, status)
+--- @param context _99.RequestContext
+--- @param status _99.Request.ResponseState
+function _99_State:finish_request(context, status)
+ local id = context.xid
local entry = self.__request_by_id[id]
- if entry then
- entry.status = status
+ if not entry then
+ return
end
+
+ entry.status = status
end
---- @param id number
-function _99_State:remove_request(id)
- for i, entry in ipairs(self.__request_history) do
- if entry.id == id then
- table.remove(self.__request_history, i)
- break
- end
+--- @param context _99.RequestContext
+---@param data _99.RequestEntry.Data
+function _99_State:add_data(context, data)
+ local id = context.xid
+ local entry = self.__request_by_id[id]
+ if not entry then
+ return
end
- self.__request_by_id[id] = nil
+ local logger = Logger:set_id(id)
+ logger:assert(
+ entry.context.operation == data.type,
+ "the data type is not the same as the operation"
+ )
+ entry.operation_data = data
end
--- @return number
function _99_State:previous_request_count()
local count = 0
for _, entry in ipairs(self.__request_history) do
- if entry.status ~= "running" then
+ if entry.status ~= "requesting" then
count = count + 1
end
end
@@ -204,31 +222,15 @@ end
function _99_State:clear_previous_requests()
local keep = {}
for _, entry in ipairs(self.__request_history) do
- if entry.status == "running" then
+ if entry.status == "requesting" then
table.insert(keep, entry)
else
- self.__request_by_id[entry.id] = nil
+ self.__request_by_id[entry.context.xid] = nil
end
end
self.__request_history = keep
end
-local _active_request_id = 0
----@param clean_up _99.Cleanup
----@param request_id number
----@param name string
----@return number
-function _99_State:add_active_request(clean_up, request_id, name)
- _active_request_id = _active_request_id + 1
- Logger:debug("adding active request", "id", _active_request_id)
- self.__active_requests[_active_request_id] = {
- clean_up = clean_up,
- request_id = request_id,
- name = name,
- }
- return _active_request_id
-end
-
--- @param mark _99.Mark
function _99_State:add_mark(mark)
table.insert(self.__active_marks, mark)
@@ -236,19 +238,25 @@ end
function _99_State:active_request_count()
local count = 0
- for _ in pairs(self.__active_requests) do
- count = count + 1
+ for _, r in pairs(self.__request_history) do
+ if r.status == "requesting" then
+ count = count + 1
+ end
end
return count
end
----@param id number
-function _99_State:remove_active_request(id)
- local logger = Logger:set_id(id)
- local r = self.__active_requests[id]
- logger:assert(r, "there is no active request for id. implementation broken")
- logger:debug("removing active request")
- self.__active_requests[id] = nil
+--- @param type "search" | "visual" | "tutorial"
+--- @return _99.RequestEntry.Data
+function _99_State:get_request_data_by_type(type)
+ local out = {}
+ for _, r in ipairs(self.__request_history) do
+ local data = r.operation_data
+ if data and data.type == type then
+ table.insert(out, data)
+ end
+ end
+ return out
end
local _99_state = _99_State.new()
@@ -275,8 +283,11 @@ end
--- @param name string
--- @param context _99.RequestContext
--- @param opts _99.ops.Opts
-local function capture_prompt(cb, name, context, opts)
+--- @param capture_content string[] | nil
+local function capture_prompt(cb, name, context, opts, capture_content)
Window.capture_input(name, {
+ content = capture_content,
+
--- @param ok boolean
--- @param response string
cb = function(ok, response)
@@ -333,6 +344,56 @@ function _99.info()
Window.display_centered_message(info)
end
+--- @param tutorials _99.RequestEntry.Data.Tutorial[]
+--- @return string[]
+local function tutorial_to_string(tutorials)
+ local out = {}
+ for _, t in ipairs(tutorials) do
+ table.insert(out, string.format("%d: %s", t.xid, t.tutorial[1]))
+ end
+ return out
+end
+
+--- @param xid number | nil
+--- @param opts? _99.window.SplitWindowOpts
+function _99.open_tutorial(xid, opts)
+ opts = opts or { split_direction = "vertical" }
+ if xid == nil then
+ local tutorials = _99_state:get_request_data_by_type("tutorial")
+ if #tutorials == 0 then
+ print("no tutorials available")
+ elseif #tutorials == 1 then
+ local data = tutorials[1].operation_data
+ assert(data, "tutorial is malformed")
+ Window.create_split(data.tutorial, data.buffer, opts)
+ else
+ local context = get_context("tutorial-lookup")
+ capture_prompt(function(_, o)
+ local response = o.additional_prompt
+ local lines = vim.split(response, "\n")
+ for _, l in ipairs(lines) do
+ local id = tonumber(vim.split(l, ":")[1])
+ if not id then
+ error(
+ "do not alter the tutoria lines, just delete the ones you dont want"
+ )
+ end
+ local tut = _99_state.__request_by_id[id]
+ local data = tut and tut.operation_data
+ assert(data and data.type == "tutorial", "invalid tutorial selected")
+ Window.create_split(data.tutorial, data.buffer, opts)
+ end
+ end, "Select Tutorial", context, {}, tutorial_to_string(tutorials))
+ end
+ return
+ end
+
+ local tutorial = _99_state.__request_by_id[xid]
+ local data = tutorial and tutorial.operation_data
+ assert(data and data.type == "tutorial", "cannot open a non tutorial")
+ Window.create_split(data.tutorial, data.buffer, opts)
+end
+
--- @param path string
function _99:rule_from_path(path)
_ = self
@@ -341,36 +402,27 @@ function _99:rule_from_path(path)
end
--- @param opts? _99.ops.SearchOpts
+--- @return number
function _99.search(opts)
local o = process_opts(opts) --[[ @as _99.ops.SearchOpts ]]
local context = get_context("search")
if o.additional_prompt then
ops.search(context, o)
- return
else
capture_prompt(ops.search, "Search", context, o)
end
+ return context.xid
end
--- @param opts _99.ops.Opts
-function _99.visual_prompt(opts)
- vim.notify(
- "use visual, visual_prompt has been deprecated",
- vim.log.levels.WARN
- )
- _99.visual(opts)
-end
-
-function _99.fill_in_function()
- error(
- "function has been removed. Just use visual. I really hate fill in function, sorry :)"
- )
-end
-
-function _99.fill_in_function_prompt()
- error(
- "function has been removed. Just use visual. I really hate fill in function, sorry :)"
- )
+function _99.tutorial(opts)
+ opts = process_opts(opts)
+ local context = get_context("tutorial")
+ if opts.additional_prompt then
+ ops.tutorial(context, opts)
+ else
+ capture_prompt(ops.tutorial, "Tutorial", context, opts)
+ end
end
--- @param opts _99.ops.Opts?
@@ -421,12 +473,18 @@ function _99.next_request_logs()
Window.display_full_screen_message(logs[_99_state.__view_log_idx])
end
+--- @class _99.QFixEntry
+--- @field filename string
+--- @field lnum number
+--- @field col number
+--- @field text string
+
function _99.stop_all_requests()
- for _, active in pairs(_99_state.__active_requests) do
- _99_state:remove_request(active.request_id)
- active.clean_up()
+ for _, request in pairs(_99_state.__request_by_id) do
+ if request.status == "requesting" then
+ request.context:stop()
+ end
end
- _99_state.__active_requests = {}
end
function _99.clear_all_marks()
@@ -436,17 +494,18 @@ function _99.clear_all_marks()
_99_state.__active_marks = {}
end
-function _99.previous_requests_to_qfix()
- local items = {}
- for _, entry in ipairs(_99_state.__request_history) do
- table.insert(items, {
- filename = entry.filename,
- lnum = entry.lnum,
- col = entry.col,
- text = string.format("[%s] %s", entry.status, entry.operation),
- })
- end
- vim.fn.setqflist({}, "r", { title = "99 Requests", items = items })
+--- @param xid number | nil
+function _99.qfix_search_results(xid)
+ --- @type _99.RequestEntry
+ local entry = _99_state.__request_by_id[xid]
+ assert(entry, "qfix_search_results could not find id: " .. xid)
+
+ local data = entry.operation_data
+ assert(data, "there must be data associated with request entry")
+ assert(data.type == "search", "the operation_data must be type search")
+
+ local items = data.qfix_items
+ vim.fn.setqflist({}, "r", { title = "99 Search Results", items = items })
vim.cmd("copen")
end
@@ -497,11 +556,15 @@ local function show_in_flight_requests()
return shut_down_in_flight_requests_window()
end
+ --- @type string[]
local lines = {
throb .. " requests(" .. tostring(count) .. ") " .. throb,
}
- for _, r in pairs(_99_state.__active_requests) do
- table.insert(lines, r.name)
+
+ for _, r in pairs(_99_state.__request_by_id) do
+ if r.status == "requesting" then
+ table.insert(lines, r.context.operation)
+ end
end
Window.resize(win, #lines[1], #lines)
@@ -562,6 +625,11 @@ function _99.setup(opts)
end
end
+ if opts.tmp_dir then
+ assert(type(opts.tmp_dir) == "string", "opts.tmp_dir must be a string")
+ end
+ _99_state.tmp_dir = opts.tmp_dir
+
_99_state.display_errors = opts.display_errors or false
_99_state:refresh_rules()
Languages.initialize(_99_state)
@@ -627,4 +695,7 @@ function _99.__debug()
end
_99.Providers = Providers
+_99.Extensions = {
+ Worker = require("99.extensions.work.worker"),
+}
return _99