summaryrefslogtreecommitdiff
path: root/lua/99/state.lua
blob: 790d0ed82155bf6f1cf56fa22bab3f50e3ca7e68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
local Agents = require("99.extensions.agents")
local Extensions = require("99.extensions")

--- @class _99.StateProps
--- @field model string
--- @field md_files string[]
--- @field prompts _99.Prompts
--- @field ai_stdout_rows number
--- @field show_in_flight_requests boolean
--- @field languages string[]
--- @field display_errors boolean
--- @field auto_add_skills boolean
--- @field provider_override _99.Providers.BaseProvider | nil
--- @field __view_log_idx number
--- @field __tmp_dir string | nil

--- 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 prompts _99.Prompts
--- @field ai_stdout_rows number
--- @field languages string[]
--- @field display_errors boolean
--- @field show_in_flight_requests boolean
--- @field show_in_flight_requests_window _99.window.Window | nil
--- @field show_in_flight_requests_throbber _99.Throbber | nil
--- @field provider_override _99.Providers.BaseProvider?
--- @field auto_add_skills boolean
--- @field rules _99.Agents.Rules
--- @field __view_log_idx number
--- @field __request_history _99.Prompt[]
--- @field __request_by_id table<number, _99.Prompt>
--- @field __active_marks _99.Mark[]
--- @field __tmp_dir string | nil
local State = {}
State.__index = State

--- @return _99.StateProps
local function create()
  return {
    model = "opencode/claude-sonnet-4-5",
    md_files = {},
    prompts = require("99.prompt-settings"),
    ai_stdout_rows = 3,
    show_in_flight_requests = false,
    languages = { "lua", "go", "java", "elixir", "cpp", "ruby" },
    display_errors = false,
    provider_override = nil,
    auto_add_skills = false,
    __view_log_idx = 1,
    __request_history = {},
    __request_by_id = {},
    tmp_dir = nil,
  }
end

--- @param opts _99.Options
--- @return _99.State
function State.new(opts)
  local props = create()
  local _99_state = setmetatable(props, State) --[[@as _99.State]]

  _99_state.show_in_flight_requests = opts.show_in_flight_requests or false
  _99_state.provider_override = opts.provider
  _99_state.completion = opts.completion
    or {
      source = nil,
      custom_rules = {},
    }
  _99_state.completion.custom_rules = _99_state.completion.custom_rules or {}
  _99_state.auto_add_skills = opts.auto_add_skills or false
  _99_state.completion.files = _99_state.completion.files or {}

  return _99_state
end

--- @return string
function State:tmp_dir()
  local tmp_dir = self.__tmp_dir or "./tmp"
  if tmp_dir then
    tmp_dir = vim.fn.expand(tmp_dir)
  end
  return tmp_dir
end

--- TODO: This is something to understand.  I bet that this is going to need
--- a lot of performance tuning.  I am just reading every file, and this could
--- take a decent amount of time if there are lots of rules.
---
--- Simple perfs:
--- 1. read 4096 bytes at a tiem instead of whole file and parse out lines
--- 2. don't show the docs
--- 3. do the operation once at setup instead of every time.
---    likely not needed to do this all the time.
function State:refresh_rules()
  self.rules = Agents.rules(self)
  Extensions.refresh(self)
end

--- @param context _99.Prompt
function State:track_prompt_request(context)
  assert(context:valid(), "context is not valid")
  table.insert(self.__request_history, context)
  self.__request_by_id[context.xid] = context
end

--- @return number
function State:completed_prompts()
  local count = 0
  for _, entry in ipairs(self.__request_history) do
    if entry.state ~= "requesting" then
      count = count + 1
    end
  end
  return count
end

function State:clear_history()
  local keep = {}
  for _, entry in ipairs(self.__request_history) do
    if entry.state == "requesting" then
      table.insert(keep, entry)
    else
      self.__request_by_id[entry.xid] = nil
    end
  end
  self.__request_history = keep
end

--- @param mark _99.Mark
function State:add_mark(mark)
  table.insert(self.__active_marks, mark)
end

--- @param mark _99.Mark
function State:clear_marks(mark)
  for _, active_mark in ipairs(self.__active_marks or {}) do
    active_mark:delete()
  end
  self.__active_marks = {}
end

function State:active_request_count()
  local count = 0
  for _, r in pairs(self.__request_history) do
    if r.state == "requesting" then
      count = count + 1
    end
  end
  return count
end

--- @param type "search" | "visual" | "tutorial"
--- @return _99.Prompt.Data
function State:get_request_data_by_type(type)
  local out = {}
  for _, r in ipairs(self.__request_history) do
    local data = r.data
    if data and data.type == type then
      table.insert(out, data)
    end
  end
  return out
end

return State