diff --git a/README.md b/README.md index d58a8851..9d7a9537 100644 --- a/README.md +++ b/README.md @@ -655,6 +655,12 @@ Example keymap for silent add: ['oY'] = { 'add_visual_selection', { open_input = false }, mode = {'v'} } ``` +Example keymap for toggling a specific context using a parameterized API action: + +```lua +['ocf'] = { 'toggle_context#current_file', desc = 'Toggle current file context' } +``` + ### Run opts You can pass additional options when running a prompt via command or API: diff --git a/lua/opencode/api.lua b/lua/opencode/api.lua index 41235179..405665e3 100644 --- a/lua/opencode/api.lua +++ b/lua/opencode/api.lua @@ -236,6 +236,12 @@ function M.quick_chat(message, range) quick_chat.quick_chat(prompt, { context_config = ctx }, range) end +---@param context_key OpencodeToggleableContextKey +---@return boolean|nil enabled +function M.toggle_context(context_key) + return require('opencode.context').toggle_context(context_key) +end + function M.toggle_pane() ui.toggle_pane() end diff --git a/lua/opencode/keymap.lua b/lua/opencode/keymap.lua index fa1f7fa4..40b665d2 100644 --- a/lua/opencode/keymap.lua +++ b/lua/opencode/keymap.lua @@ -26,8 +26,14 @@ local function process_keymap_entry(keymap_config, default_modes, base_opts, def -- Skip keymap if explicitly set to false (disabled) elseif config_entry then local func_name = config_entry[1] - local func_args = config_entry[2] - local raw_callback = type(func_name) == 'function' and func_name or api[func_name] + local resolved_func_name = func_name + local inline_arg = nil + if type(func_name) == 'string' then + resolved_func_name, inline_arg = func_name:match('^([^#]+)#(.+)$') + resolved_func_name = resolved_func_name or func_name + end + local func_args = config_entry[2] or inline_arg + local raw_callback = type(func_name) == 'function' and func_name or api[resolved_func_name] local callback = raw_callback if raw_callback and func_args then @@ -38,7 +44,7 @@ local function process_keymap_entry(keymap_config, default_modes, base_opts, def local modes = config_entry.mode or default_modes local opts = vim.tbl_deep_extend('force', {}, base_opts) - opts.desc = config_entry.desc or cmds[func_name] and cmds[func_name].desc + opts.desc = config_entry.desc or cmds[resolved_func_name] and cmds[resolved_func_name].desc if callback then if defer_to_completion then diff --git a/lua/opencode/types.lua b/lua/opencode/types.lua index 6ad6aabf..a3f1fa56 100644 --- a/lua/opencode/types.lua +++ b/lua/opencode/types.lua @@ -65,7 +65,7 @@ ---@field share? SessionShareInfo ---@class OpencodeKeymapEntry ----@field [1] string # Function name +---@field [1] string # Function name, optionally suffixed with # to pass one string arg ---@field mode? string|string[] # Mode(s) for the keymap ---@field desc? string # Keymap description diff --git a/tests/unit/api_spec.lua b/tests/unit/api_spec.lua index d77babe9..85cea520 100644 --- a/tests/unit/api_spec.lua +++ b/tests/unit/api_spec.lua @@ -1,6 +1,7 @@ local api = require('opencode.api') local core = require('opencode.core') local ui = require('opencode.ui.ui') +local context = require('opencode.context') local state = require('opencode.state') local stub = require('luassert.stub') local assert = require('luassert') @@ -104,6 +105,12 @@ describe('opencode.api', function() new_session = true, focus = 'output', }) + + assert.is_function(api.toggle_context, 'Should export toggle_context') + stub(context, 'toggle_context').returns(true) + local enabled = api.toggle_context('current_file') + assert.is_true(enabled) + assert.stub(context.toggle_context).was_called_with('current_file') end) end) diff --git a/tests/unit/keymap_spec.lua b/tests/unit/keymap_spec.lua index 9acedda2..5f60cfd6 100644 --- a/tests/unit/keymap_spec.lua +++ b/tests/unit/keymap_spec.lua @@ -39,6 +39,7 @@ describe('opencode.keymap', function() mock_api = { open_input = function() end, toggle = function() end, + toggle_context = function() end, submit_input_prompt = function() end, permission_accept = function() end, permission_accept_all = function() end, @@ -46,6 +47,7 @@ describe('opencode.keymap', function() commands = { open_input = { desc = 'Open input window' }, toggle = { desc = 'Toggle opencode windows' }, + toggle_context = { desc = 'Toggle prompt context' }, submit_input_prompt = { desc = 'Submit input prompt' }, }, } @@ -159,6 +161,27 @@ describe('opencode.keymap', function() assert.is_not_nil(keymap_entry.opts.desc, 'Should have a description from API fallback') assert.equal('Toggle opencode windows', keymap_entry.opts.desc) end) + + it('supports parameterized function names with #suffix', function() + local called_with = nil + mock_api.toggle_context = function(context_key) + called_with = context_key + end + + local test_keymap = { + editor = { + ['tcf'] = { 'toggle_context#current_file' }, + }, + } + + keymap.setup(test_keymap) + + assert.equal(1, #set_keymaps, 'Should set up 1 keymap') + assert.equal('Toggle prompt context', set_keymaps[1].opts.desc) + + set_keymaps[1].callback() + assert.equal('current_file', called_with) + end) end) describe('setup_window_keymaps', function()