Skip to content

Commit ea03234

Browse files
committed
feat(context): expose public API
1 parent 3e890ae commit ea03234

5 files changed

Lines changed: 131 additions & 15 deletions

File tree

lua/opencode/context.lua

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@ local BaseContext = require('opencode.context.base_context')
99

1010
local M = {}
1111

12+
---@type table<OpencodeToggleableContextKey, boolean>
13+
local toggleable_context_keys = {
14+
current_file = true,
15+
selection = true,
16+
diagnostics = true,
17+
cursor_data = true,
18+
buffer = true,
19+
git_diff = true,
20+
}
21+
22+
---@param context_key OpencodeToggleableContextKey
23+
---@return table
24+
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]
27+
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]
30+
end
31+
1232
M.ChatContext = ChatContext
1333
M.QuickChatContext = QuickChatContext
1434

@@ -47,6 +67,33 @@ function M.is_context_enabled(context_key, context_config)
4767
return BaseContext.is_context_enabled(context_key, context_config)
4868
end
4969

70+
---@param context_key OpencodeToggleableContextKey
71+
---@param enabled boolean
72+
---@return boolean|nil enabled
73+
function M.set_context(context_key, enabled)
74+
if not toggleable_context_keys[context_key] then
75+
return nil
76+
end
77+
78+
local context_state = ensure_context_state(context_key)
79+
context_state.enabled = enabled
80+
81+
M.load()
82+
83+
return enabled
84+
end
85+
86+
---@param context_key OpencodeToggleableContextKey
87+
---@return boolean|nil enabled
88+
function M.toggle_context(context_key)
89+
if not toggleable_context_keys[context_key] then
90+
return nil
91+
end
92+
93+
local enabled = not M.is_context_enabled(context_key)
94+
return M.set_context(context_key, enabled)
95+
end
96+
5097
function M.get_diagnostics(buf, context_config, range)
5198
return BaseContext.get_diagnostics(buf, context_config, range)
5299
end

lua/opencode/types.lua

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@
165165
---@field buffer { enabled: boolean }
166166
---@field git_diff { enabled: boolean }
167167

168+
---@alias OpencodeToggleableContextKey
169+
---| 'current_file'
170+
---| 'selection'
171+
---| 'diagnostics'
172+
---| 'cursor_data'
173+
---| 'buffer'
174+
---| 'git_diff'
175+
168176
---@class OpencodeDebugConfig
169177
---@field enabled boolean
170178
---@field capture_streamed_events boolean

lua/opencode/ui/completion/context.lua

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
local config = require('opencode.config')
22
local context = require('opencode.context')
3-
local state = require('opencode.state')
43
local icons = require('opencode.ui.icons')
54
local Promise = require('opencode.promise')
65

@@ -267,18 +266,10 @@ local context_source = {
267266
end
268267

269268
local type = item.data.type
270-
local context_cfg = vim.deepcopy(state.current_context_config or {})
271-
if not context_cfg or not context_cfg[type] then
272-
context_cfg[type] = vim.deepcopy(config.context[type])
273-
end
274-
275269
if vim.tbl_contains({ 'current_file', 'selection', 'diagnostics', 'cursor_data' }, type) then
276-
context_cfg[type] = context_cfg[type] or {}
277-
context_cfg[type].enabled = not item.data.available
270+
context.toggle_context(type)
278271
end
279272

280-
state.current_context_config = context_cfg
281-
282273
if type == 'mentioned_file' then
283274
local file_path = item.data.additional_data and item.data.additional_data.file_path or item.data.name
284275
context.remove_file(file_path)

tests/unit/context_completion_spec.lua

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ describe('context completion', function()
5353
remove_file = function() end,
5454
remove_subagent = function() end,
5555
remove_selection = function() end,
56+
toggle_context = function(type)
57+
mock_state.current_context_config = mock_state.current_context_config or {}
58+
mock_state.current_context_config[type] = mock_state.current_context_config[type] or {}
59+
mock_state.current_context_config[type].enabled = not mock_context.is_context_enabled(type)
60+
return true
61+
end,
5662
get_context = function()
5763
return {
5864
current_file = { extension = 'lua', name = 'test.lua', path = '/test/test.lua' },
@@ -300,6 +306,7 @@ describe('context completion', function()
300306
end)
301307

302308
it('should toggle context enabled state for toggleable items', function()
309+
local toggle_called = false
303310
local item = {
304311
label = 'Current File',
305312
data = {
@@ -309,13 +316,16 @@ describe('context completion', function()
309316
},
310317
}
311318

312-
source.on_complete(item)
319+
local context_module = require('opencode.context')
320+
context_module.toggle_context = function(type)
321+
toggle_called = true
322+
assert.are.equal('current_file', type)
323+
return true
324+
end
313325

314-
local state_module = require('opencode.state')
326+
source.on_complete(item)
315327

316-
assert.is_not_nil(state_module.current_context_config)
317-
assert.is_not_nil(state_module.current_context_config.current_file)
318-
assert.is_false(state_module.current_context_config.current_file.enabled)
328+
assert.is_true(toggle_called)
319329
end)
320330

321331
it('should remove mentioned file when selected', function()

tests/unit/context_spec.lua

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,66 @@ describe('context static API with config override', function()
321321
end)
322322
end)
323323

324+
describe('context toggle API', function()
325+
local original_context_config
326+
local original_load
327+
328+
before_each(function()
329+
original_context_config = vim.deepcopy(state.current_context_config)
330+
original_load = context.load
331+
end)
332+
333+
after_each(function()
334+
state.current_context_config = original_context_config
335+
context.load = original_load
336+
end)
337+
338+
it('set_context updates state config for a supported key', function()
339+
local load_called = false
340+
context.load = function()
341+
load_called = true
342+
end
343+
344+
local enabled = context.set_context('current_file', false)
345+
346+
assert.is_false(enabled)
347+
assert.is_false(state.current_context_config.current_file.enabled)
348+
assert.is_true(load_called)
349+
end)
350+
351+
it('toggle_context inverts the current value', function()
352+
context.load = function() end
353+
state.current_context_config = {
354+
current_file = { enabled = false },
355+
}
356+
357+
local enabled = context.toggle_context('current_file')
358+
359+
assert.is_true(enabled)
360+
assert.is_true(state.current_context_config.current_file.enabled)
361+
end)
362+
363+
it('set_context supports buffer and git_diff keys', function()
364+
context.load = function() end
365+
366+
local enabled_buffer = context.set_context('buffer', true)
367+
local enabled_git_diff = context.set_context('git_diff', true)
368+
369+
assert.is_true(enabled_buffer)
370+
assert.is_true(enabled_git_diff)
371+
assert.is_true(state.current_context_config.buffer.enabled)
372+
assert.is_true(state.current_context_config.git_diff.enabled)
373+
end)
374+
375+
it('returns error for unsupported keys', function()
376+
context.load = function() end
377+
378+
local enabled = context.toggle_context('files')
379+
380+
assert.is_nil(enabled)
381+
end)
382+
end)
383+
324384
describe('get_diagnostics with chat context selections', function()
325385
local ChatContext
326386

0 commit comments

Comments
 (0)