Skip to content

Commit 951df8c

Browse files
committed
refactor(state): centralize protected state mutations into domain setters
1 parent 62f8dd6 commit 951df8c

42 files changed

Lines changed: 477 additions & 429 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lua/opencode/context.lua

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ local toggleable_context_keys = {
2222
---@param context_key OpencodeToggleableContextKey
2323
---@return table
2424
local function ensure_context_state(context_key)
25-
state.current_context_config = state.current_context_config or {}
26-
local current = state.current_context_config[context_key]
25+
local current_config = state.current_context_config or {}
26+
local current = current_config[context_key]
2727
local defaults = vim.tbl_get(config, 'context', context_key) or {}
28-
state.current_context_config[context_key] = vim.tbl_deep_extend('force', {}, defaults, current or {})
29-
return state.current_context_config[context_key]
28+
current_config[context_key] = vim.tbl_deep_extend('force', {}, defaults, current or {})
29+
state.context.set_current_context_config(current_config)
30+
return current_config[context_key]
3031
end
3132

3233
M.ChatContext = ChatContext
@@ -117,12 +118,12 @@ end
117118
-- Delegate global state management to ChatContext
118119
function M.add_selection(selection)
119120
ChatContext.add_selection(selection)
120-
state.context_updated_at = vim.uv.now()
121+
state.context.set_context_updated_at(vim.uv.now())
121122
end
122123

123124
function M.remove_selection(selection)
124125
ChatContext.remove_selection(selection)
125-
state.context_updated_at = vim.uv.now()
126+
state.context.set_context_updated_at(vim.uv.now())
126127
end
127128

128129
function M.clear_selections()
@@ -180,13 +181,13 @@ function M.add_file(file)
180181

181182
file = vim.fn.fnamemodify(file, ':p')
182183
ChatContext.add_file(file)
183-
state.context_updated_at = vim.uv.now()
184+
state.context.set_context_updated_at(vim.uv.now())
184185
end
185186

186187
function M.remove_file(file)
187188
file = vim.fn.fnamemodify(file, ':p')
188189
ChatContext.remove_file(file)
189-
state.context_updated_at = vim.uv.now()
190+
state.context.set_context_updated_at(vim.uv.now())
190191
end
191192

192193
function M.clear_files()
@@ -195,12 +196,12 @@ end
195196

196197
function M.add_subagent(subagent)
197198
ChatContext.add_subagent(subagent)
198-
state.context_updated_at = vim.uv.now()
199+
state.context.set_context_updated_at(vim.uv.now())
199200
end
200201

201202
function M.remove_subagent(subagent)
202203
ChatContext.remove_subagent(subagent)
203-
state.context_updated_at = vim.uv.now()
204+
state.context.set_context_updated_at(vim.uv.now())
204205
end
205206

206207
function M.clear_subagents()
@@ -213,7 +214,7 @@ end
213214

214215
function M.load()
215216
ChatContext.load()
216-
state.context_updated_at = vim.uv.now()
217+
state.context.set_context_updated_at(vim.uv.now())
217218
end
218219

219220
-- Context creation with delta logic (delegates to ChatContext)

lua/opencode/context/chat_context.lua

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ function M.add_selection(selection)
175175
end
176176

177177
table.insert(M.context.selections, selection)
178-
state.context_updated_at = vim.uv.now()
178+
state.context.set_context_updated_at(vim.uv.now())
179179
end
180180

181181
function M.remove_selection(selection)
@@ -190,12 +190,12 @@ function M.remove_selection(selection)
190190
break
191191
end
192192
end
193-
state.context_updated_at = vim.uv.now()
193+
state.context.set_context_updated_at(vim.uv.now())
194194
end
195195

196196
function M.clear_selections()
197197
M.context.selections = {}
198-
state.context_updated_at = vim.uv.now()
198+
state.context.set_context_updated_at(vim.uv.now())
199199
end
200200

201201
function M.add_file(file)
@@ -210,7 +210,7 @@ function M.add_file(file)
210210
if not vim.tbl_contains(M.context.mentioned_files, file) then
211211
table.insert(M.context.mentioned_files, file)
212212
end
213-
state.context_updated_at = vim.uv.now()
213+
state.context.set_context_updated_at(vim.uv.now())
214214
end
215215

216216
function M.remove_file(file)
@@ -226,12 +226,12 @@ function M.remove_file(file)
226226
break
227227
end
228228
end
229-
state.context_updated_at = vim.uv.now()
229+
state.context.set_context_updated_at(vim.uv.now())
230230
end
231231

232232
function M.clear_files()
233233
M.context.mentioned_files = {}
234-
state.context_updated_at = vim.uv.now()
234+
state.context.set_context_updated_at(vim.uv.now())
235235
end
236236

237237
function M.add_subagent(subagent)
@@ -243,7 +243,7 @@ function M.add_subagent(subagent)
243243
if not vim.tbl_contains(M.context.mentioned_subagents, subagent) then
244244
table.insert(M.context.mentioned_subagents, subagent)
245245
end
246-
state.context_updated_at = vim.uv.now()
246+
state.context.set_context_updated_at(vim.uv.now())
247247
end
248248

249249
function M.remove_subagent(subagent)
@@ -258,18 +258,18 @@ function M.remove_subagent(subagent)
258258
break
259259
end
260260
end
261-
state.context_updated_at = vim.uv.now()
261+
state.context.set_context_updated_at(vim.uv.now())
262262
end
263263

264264
function M.clear_subagents()
265265
M.context.mentioned_subagents = {}
266-
state.context_updated_at = vim.uv.now()
266+
state.context.set_context_updated_at(vim.uv.now())
267267
end
268268

269269
function M.unload_attachments()
270270
M.context.mentioned_files = {}
271271
M.context.selections = {}
272-
state.context_updated_at = vim.uv.now()
272+
state.context.set_context_updated_at(vim.uv.now())
273273
end
274274

275275
function M.get_mentioned_files()
@@ -402,7 +402,7 @@ function M.load()
402402
or not vim.deep_equal(prev_cursor_data, M.context.cursor_data)
403403
or not vim.deep_equal(prev_linter_errors, M.context.linter_errors)
404404
then
405-
state.context_updated_at = vim.uv.now()
405+
state.context.set_context_updated_at(vim.uv.now())
406406
end
407407

408408
-- Handle current selection
@@ -471,7 +471,7 @@ function M.delta_context(opts)
471471
end
472472
end
473473

474-
state.context_updated_at = vim.uv.now()
474+
state.context.set_context_updated_at(vim.uv.now())
475475
return ctx
476476
end
477477

lua/opencode/core.lua

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ M.check_cwd = function()
7272
'CWD changed since last check, resetting session and context',
7373
{ current_cwd = state.current_cwd, new_cwd = vim.fn.getcwd() }
7474
)
75-
state.current_cwd = vim.fn.getcwd()
75+
state.context.set_current_cwd(vim.fn.getcwd())
7676
state.session.clear_active()
7777
context.unload_attachments()
7878
end
@@ -130,7 +130,7 @@ M.open = Promise.async(function(opts)
130130
local ok, err = pcall(function()
131131
if opts.new_session then
132132
state.session.clear_active()
133-
state.last_sent_context = nil
133+
state.session.set_last_sent_context(nil)
134134
context.unload_attachments()
135135

136136
M.ensure_current_mode():await()
@@ -187,7 +187,7 @@ M.send_message = Promise.async(function(prompt, opts)
187187
opts = opts or {}
188188

189189
opts.context = vim.tbl_deep_extend('force', state.current_context_config or {}, opts.context or {})
190-
state.current_context_config = opts.context
190+
state.context.set_current_context_config(opts.context)
191191
context.load()
192192
opts.model = opts.model or M.initialize_current_model():await()
193193
opts.agent = opts.agent or state.current_mode or config.default_mode
@@ -223,7 +223,7 @@ M.send_message = Promise.async(function(prompt, opts)
223223
local sent_message_count = vim.deepcopy(state.user_message_count)
224224
local new_value = (sent_message_count[session_id] or 0) + num
225225
sent_message_count[session_id] = new_value >= 0 and new_value or 0
226-
state.user_message_count = sent_message_count
226+
state.session.set_user_message_count(sent_message_count)
227227
end
228228

229229
update_sent_message_count(1)
@@ -267,7 +267,7 @@ end)
267267
---@param prompt string
268268
function M.after_run(prompt)
269269
context.unload_attachments()
270-
state.last_sent_context = vim.deepcopy(context.get_context())
270+
state.session.set_last_sent_context(vim.deepcopy(context.get_context()))
271271
context.delta_context()
272272
require('opencode.history').write(prompt)
273273
M._abort_count = 0
@@ -437,7 +437,7 @@ M.opencode_ok = Promise.async(function()
437437
if not state.opencode_cli_version or state.opencode_cli_version == '' then
438438
local result = Promise.system({ config.opencode_executable, '--version' }):await()
439439
local out = (result and result.stdout or ''):gsub('%s+$', '')
440-
state.opencode_cli_version = out:match('(%d+%%.%d+%%.%d+)') or out
440+
state.jobs.set_opencode_cli_version(out:match('(%d+%%.%d+%%.%d+)') or out)
441441
end
442442

443443
local required = state.required_version
@@ -581,7 +581,7 @@ M.handle_directory_change = Promise.async(function()
581581
vim.notify('Loading last session for new working dir [' .. cwd .. ']', vim.log.levels.INFO)
582582

583583
state.session.clear_active()
584-
state.last_sent_context = nil
584+
state.session.set_last_sent_context(nil)
585585
context.unload_attachments()
586586

587587
state.session.set_active(session.get_last_workspace_session():await() or M.create_new_session():await())
@@ -615,7 +615,7 @@ function M.setup()
615615
M.opencode_ok()
616616
end)
617617
local OpencodeApiClient = require('opencode.api_client')
618-
state.api_client = OpencodeApiClient.create()
618+
state.jobs.set_api_client(OpencodeApiClient.create())
619619
end
620620

621621
return M

lua/opencode/event_manager.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,9 @@ function EventManager:get_subscriber_count(event_name)
593593
end
594594

595595
function EventManager.setup()
596-
state.event_manager = EventManager.new()
597-
state.event_manager:start()
596+
local manager = EventManager.new()
597+
state.jobs.set_event_manager(manager)
598+
manager:start()
598599
end
599600

600601
return EventManager

lua/opencode/image_handler.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ function M.paste_image_from_clipboard()
150150

151151
if success then
152152
context.add_file(image_path)
153-
state.context_updated_at = os.time()
153+
state.context.set_context_updated_at(os.time())
154154
vim.notify('Image saved and added to context: ' .. vim.fn.fnamemodify(image_path, ':t'), vim.log.levels.INFO)
155155
return true
156156
end

lua/opencode/state/context.lua

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
local store = require('opencode.state.store')
2+
3+
local M = {}
4+
5+
---@param config OpencodeContextConfig|nil
6+
function M.set_current_context_config(config)
7+
return store.set('current_context_config', config)
8+
end
9+
10+
---@param timestamp number|nil
11+
function M.set_context_updated_at(timestamp)
12+
return store.set('context_updated_at', timestamp)
13+
end
14+
15+
---@param cwd string|nil
16+
function M.set_current_cwd(cwd)
17+
return store.set('current_cwd', cwd)
18+
end
19+
20+
return M

lua/opencode/state/init.lua

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,18 @@
2828
---@field clear_active fun(opts?: OpencodeProtectedStateSetOptions)
2929
---@field set_restore_points fun(points: RestorePoint[], opts?: OpencodeProtectedStateSetOptions)
3030
---@field reset_restore_points fun(opts?: OpencodeProtectedStateSetOptions)
31+
---@field set_last_sent_context fun(context: OpencodeContext|nil)
32+
---@field set_user_message_count fun(count: table<string, number>)
3133

3234
---@class OpencodeJobStateMutations
3335
---@field increment_count fun(delta?: integer, opts?: OpencodeProtectedStateSetOptions)
3436
---@field decrement_count fun(delta?: integer, opts?: OpencodeProtectedStateSetOptions)
3537
---@field set_count fun(count: integer, opts?: OpencodeProtectedStateSetOptions)
3638
---@field set_server fun(server: OpencodeServer|nil, opts?: OpencodeProtectedStateSetOptions)
3739
---@field clear_server fun(opts?: OpencodeProtectedStateSetOptions)
40+
---@field set_api_client fun(client: OpencodeApiClient|nil)
41+
---@field set_event_manager fun(manager: EventManager|nil)
42+
---@field set_opencode_cli_version fun(version: string|nil)
3843

3944
---@class OpencodeUiStateMutations
4045
---@field set_windows fun(windows: OpencodeWindowState|nil)
@@ -48,6 +53,9 @@
4853
---@field set_current_code_buf fun(bufnr: integer|nil)
4954
---@field set_last_window_width_ratio fun(ratio: number|nil)
5055
---@field clear_last_window_width_ratio fun()
56+
---@field set_input_content fun(lines: table)
57+
---@field set_saved_window_options fun(opts: table|nil)
58+
---@field set_pre_zoom_width fun(width: integer|nil)
5159

5260
---@class OpencodeModelStateMutations
5361
---@field set_mode fun(mode: string|nil)
@@ -60,6 +68,19 @@
6068
---@field set_mode_model_map fun(mode_map: table<string, string>)
6169
---@field set_mode_model_override fun(mode: string, model: string)
6270

71+
---@class OpencodeRendererStateMutations
72+
---@field set_messages fun(messages: OpencodeMessage[]|nil)
73+
---@field set_current_message fun(message: OpencodeMessage|nil)
74+
---@field set_last_user_message fun(message: OpencodeMessage|nil)
75+
---@field set_pending_permissions fun(permissions: OpencodePermission[])
76+
---@field set_cost fun(cost: number)
77+
---@field set_tokens_count fun(count: number)
78+
79+
---@class OpencodeContextStateMutations
80+
---@field set_current_context_config fun(config: OpencodeContextConfig|nil)
81+
---@field set_context_updated_at fun(timestamp: number|nil)
82+
---@field set_current_cwd fun(cwd: string|nil)
83+
6384
---@class OpencodeState
6485
---@field windows OpencodeWindowState|nil
6586
---@field is_opening boolean
@@ -109,29 +130,30 @@
109130
---@field jobs OpencodeJobStateMutations
110131
---@field ui OpencodeUiStateMutations
111132
---@field model OpencodeModelStateMutations
133+
---@field renderer OpencodeRendererStateMutations
134+
---@field context OpencodeContextStateMutations
112135

113136
local store = require('opencode.state.store')
114137
local session = require('opencode.state.session')
115138
local jobs = require('opencode.state.jobs')
116139
local ui = require('opencode.state.ui')
117140
local model = require('opencode.state.model')
118-
local test_helpers = require('opencode.state.test_helpers')
141+
local renderer = require('opencode.state.renderer')
142+
local context = require('opencode.state.context')
119143

120144
local M = {
121145
store = store,
122146
session = session,
123147
jobs = jobs,
124148
ui = ui,
125149
model = model,
126-
test_helpers = test_helpers,
150+
renderer = renderer,
151+
context = context,
127152
subscribe = store.subscribe,
128153
unsubscribe = store.unsubscribe,
129154
notify = store.notify,
130155
append = store.append,
131156
remove = store.remove,
132-
set_raw = store.set_raw,
133-
allow_raw_writes_for_tests = test_helpers.allow_raw_writes_for_tests,
134-
silence_protected_writes = test_helpers.silence_protected_writes,
135157
}
136158

137159
function M.is_running()
@@ -142,8 +164,8 @@ return setmetatable(M, {
142164
__index = function(_, key)
143165
return store.get(key)
144166
end,
145-
__newindex = function(_, key, value)
146-
store.set(key, value, { source = 'raw' })
167+
__newindex = function(_, key, _value)
168+
error(string.format('Direct write to state key `%s` is not allowed; use a state domain setter', key), 2)
147169
end,
148170
__pairs = function()
149171
return pairs(store.state())

lua/opencode/state/jobs.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,19 @@ function M.clear_server(opts)
3535
return store.set('opencode_server', nil, opts)
3636
end
3737

38+
---@param client OpencodeApiClient|nil
39+
function M.set_api_client(client)
40+
return store.set('api_client', client)
41+
end
42+
43+
---@param manager EventManager|nil
44+
function M.set_event_manager(manager)
45+
return store.set('event_manager', manager)
46+
end
47+
48+
---@param version string|nil
49+
function M.set_opencode_cli_version(version)
50+
return store.set('opencode_cli_version', version)
51+
end
52+
3853
return M

0 commit comments

Comments
 (0)