Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions lua/opencode/keymap.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
local M = {}

-- Helper function to process keymap entries
local function is_completion_visible()
local ok, completion = pcall(require, 'opencode.ui.completion')
return ok and completion.is_visible()
end

local function wrap_with_completion_check(key_binding, callback)
return function()
if is_completion_visible() then
return vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key_binding, true, false, true), 'n', false)
end
return callback()
end
end

---@param keymap_config table The keymap configuration table
---@param default_modes table Default modes for these keymaps
---@param base_opts table Base options to use for all keymaps
local function process_keymap_entry(keymap_config, default_modes, base_opts)
---@param defer_to_completion boolean? Whether to defer to completion engine when visible
local function process_keymap_entry(keymap_config, default_modes, base_opts, defer_to_completion)
local api = require('opencode.api')
local cmds = api.commands

Expand All @@ -19,6 +33,9 @@ local function process_keymap_entry(keymap_config, default_modes, base_opts)
opts.desc = config_entry.desc or cmds[func_name] and cmds[func_name].desc

if callback then
if defer_to_completion then
callback = wrap_with_completion_check(key_binding, callback)
end
vim.keymap.set(modes, key_binding, callback, opts)
else
vim.notify(string.format('No action found for keymap: %s -> %s', key_binding, func_name), vim.log.levels.WARN)
Expand All @@ -34,15 +51,15 @@ function M.setup(keymap)
process_keymap_entry(keymap.editor or {}, { 'n', 'v' }, { silent = false })
end

-- Setup window-specific keymaps (shared helper for input/output windows)
---@param keymap_config table Window keymap configuration
---@param buf_id integer Buffer ID to set keymaps for
function M.setup_window_keymaps(keymap_config, buf_id)
---@param defer_to_completion boolean? Whether to defer to completion engine when visible (default: false)
function M.setup_window_keymaps(keymap_config, buf_id, defer_to_completion)
if not vim.api.nvim_buf_is_valid(buf_id) then
return
end

process_keymap_entry(keymap_config or {}, { 'n' }, { silent = true, buffer = buf_id })
process_keymap_entry(keymap_config or {}, { 'n' }, { silent = true, buffer = buf_id }, defer_to_completion)
end

return M
7 changes: 7 additions & 0 deletions lua/opencode/ui/completion.lua
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,11 @@ function M.hide_completion()
end
end

function M.is_visible()
if M._current_engine and M._current_engine.is_visible then
return M._current_engine:is_visible()
end
return false
end

return M
19 changes: 17 additions & 2 deletions lua/opencode/ui/completion/engines/base.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,19 @@ function CompletionEngine.get_trigger_characters()
end

---Check if the completion engine is available for use
---Default implementation checks if current buffer filetype is 'opencode'
---Default implementation checks if current buffer is the opencode input buffer
---or has filetype 'opencode'.
---Child classes can override this to add engine-specific availability checks
---@return boolean true if the engine can be used in the current context
function CompletionEngine.is_available()
return vim.bo.filetype == 'opencode'
local current_buf = vim.api.nvim_get_current_buf()
if vim.bo[current_buf].filetype == 'opencode' then
return true
end

local ok, state = pcall(require, 'opencode.state')
local input_buf = ok and state.windows and state.windows.input_buf
return input_buf ~= nil and input_buf == current_buf
end

---Parse trigger characters from text before cursor
Expand Down Expand Up @@ -106,6 +114,13 @@ function CompletionEngine:trigger(trigger_char)
-- Default implementation does nothing
end

---Check if completion menu is currently visible
---Child classes should override this with engine-specific implementation
---@return boolean
function CompletionEngine:is_visible()
return false
end

---Handle completion item selection
---Called when a completion item is selected by the user
---Delegates to the completion module's on_complete handler
Expand Down
7 changes: 7 additions & 0 deletions lua/opencode/ui/completion/engines/blink_cmp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ function BlinkCmpEngine:setup(completion_sources)
return true
end

---Check if blink-cmp completion menu is visible
---@return boolean
function BlinkCmpEngine:is_visible()
local ok, blink = pcall(require, 'blink.cmp')
return ok and blink.is_visible()
end

---Trigger completion manually for blink-cmp
---@param trigger_char string
function BlinkCmpEngine:trigger(trigger_char)
Expand Down
7 changes: 7 additions & 0 deletions lua/opencode/ui/completion/engines/nvim_cmp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ function NvimCmpEngine:setup(completion_sources)
return true
end

---Check if nvim-cmp completion menu is visible
---@return boolean
function NvimCmpEngine:is_visible()
local ok, cmp = pcall(require, 'cmp')
return ok and cmp.visible()
end

---Trigger completion manually for nvim-cmp
---@param trigger_char string
function NvimCmpEngine:trigger(trigger_char)
Expand Down
6 changes: 6 additions & 0 deletions lua/opencode/ui/completion/engines/vim_complete.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ function VimCompleteEngine:setup(completion_sources)
return true
end

---Check if vim completion menu is visible
---@return boolean
function VimCompleteEngine:is_visible()
return vim.fn.pumvisible() == 1
end

---Trigger completion manually for vim
---@param trigger_char string
function VimCompleteEngine:trigger(trigger_char)
Expand Down
11 changes: 10 additions & 1 deletion lua/opencode/ui/input_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,13 @@ M._execute_slash_command = function(command)
end

function M.setup(windows)

if config.ui.input.text.wrap then
vim.api.nvim_set_option_value('wrap', true, { win = windows.input_win })
vim.api.nvim_set_option_value('linebreak', true, { win = windows.input_win })
end

vim.api.nvim_set_option_value('filetype', 'opencode', { buf = windows.input_buf })
vim.api.nvim_set_option_value('winhighlight', config.ui.window_highlight, { win = windows.input_win })
vim.api.nvim_set_option_value('signcolumn', 'yes', { win = windows.input_win })
vim.api.nvim_set_option_value('cursorline', false, { win = windows.input_win })
Expand Down Expand Up @@ -353,9 +355,16 @@ function M.is_empty()
return #lines == 0 or (#lines == 1 and lines[1] == '')
end

local keymaps_set_for_buf = {}

function M.setup_keymaps(windows)
if keymaps_set_for_buf[windows.input_buf] then
return
end
keymaps_set_for_buf[windows.input_buf] = true

local keymap = require('opencode.keymap')
keymap.setup_window_keymaps(config.keymap.input_window, windows.input_buf)
keymap.setup_window_keymaps(config.keymap.input_window, windows.input_buf, true)
end

function M.setup_autocmds(windows, group)
Expand Down
1 change: 0 additions & 1 deletion lua/opencode/ui/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ function M.focus_input(opts)

if input_window.is_hidden() then
input_window._show()
return
end

if not windows.input_win then
Expand Down