From eb4411b4d948f6fe052aeff3ebe92b6a66dc27c3 Mon Sep 17 00:00:00 2001 From: Julio Garcia Date: Sun, 26 Jul 2026 13:21:14 +0200 Subject: [PATCH 1/3] feat: replace opencode window on external buffer switch Remove winfixbuf from output/input windows so that file pickers (telescope, snacks, fyler) and :e can switch the buffer without E1513 errors. Extend the BufEnter teardown guard to all positions. When the buffer is replaced externally, keep the output window open with the new file buffer instead of closing it, so the file remains visible after opencode UI tears down. --- lua/opencode/ui/autocmds.lua | 44 +++++++++++++++---------------- lua/opencode/ui/input_window.lua | 3 --- lua/opencode/ui/output_window.lua | 3 --- lua/opencode/ui/ui.lua | 9 ++++++- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index bcc7ae35..9c32bdb7 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -97,31 +97,29 @@ function M.setup_autocmds(windows) end, }) - if require('opencode.config').ui.position == 'current' then - vim.api.nvim_create_autocmd('BufEnter', { - group = group, - callback = function() - local current_win = vim.api.nvim_get_current_win() - local current_buf = vim.api.nvim_get_current_buf() - - if current_win ~= windows.output_win and current_win ~= windows.input_win then - return - end + vim.api.nvim_create_autocmd('BufEnter', { + group = group, + callback = function() + local current_win = vim.api.nvim_get_current_win() + local current_buf = vim.api.nvim_get_current_buf() - local is_opencode_buf = ( - current_buf == windows.output_buf - or current_buf == windows.input_buf - or (windows.footer_buf and current_buf == windows.footer_buf) - ) + if current_win ~= windows.output_win and current_win ~= windows.input_win then + return + end - if not is_opencode_buf then - vim.schedule(function() - require('opencode.ui.ui').teardown_visible_windows(windows) - end) - end - end, - }) - end + local is_opencode_buf = ( + current_buf == windows.output_buf + or current_buf == windows.input_buf + or (windows.footer_buf and current_buf == windows.footer_buf) + ) + + if not is_opencode_buf then + vim.schedule(function() + require('opencode.ui.ui').teardown_visible_windows(windows) + end) + end + end, + }) end ---@param windows OpencodeWindowState? diff --git a/lua/opencode/ui/input_window.lua b/lua/opencode/ui/input_window.lua index fc159663..cec511a4 100644 --- a/lua/opencode/ui/input_window.lua +++ b/lua/opencode/ui/input_window.lua @@ -273,9 +273,6 @@ function M.setup(windows) window_options.set_buffer_option('buflisted', false, windows.input_buf) window_options.set_buffer_option('swapfile', false, windows.input_buf) - if windows.position ~= 'current' then - window_options.set_window_option('winfixbuf', true, windows.input_win) - end window_options.set_window_option('winfixwidth', true, windows.input_win) M.update_dimensions(windows) diff --git a/lua/opencode/ui/output_window.lua b/lua/opencode/ui/output_window.lua index 8833584d..16ba4f4e 100644 --- a/lua/opencode/ui/output_window.lua +++ b/lua/opencode/ui/output_window.lua @@ -287,9 +287,6 @@ function M.setup(windows) end) window_options.set_window_option('foldtext', 'v:lua.opencode_fold_text()', windows.output_win) - if windows.position ~= 'current' then - window_options.set_window_option('winfixbuf', true, windows.output_win, { save_original = true }) - end window_options.set_window_option('winfixheight', true, windows.output_win, { save_original = true }) window_options.set_window_option('winfixwidth', true, windows.output_win, { save_original = true }) window_options.set_window_option('signcolumn', 'yes', windows.output_win, { save_original = true }) diff --git a/lua/opencode/ui/ui.lua b/lua/opencode/ui/ui.lua index d969aadb..7cf0f303 100644 --- a/lua/opencode/ui/ui.lua +++ b/lua/opencode/ui/ui.lua @@ -113,10 +113,13 @@ local function close_or_restore_output_window(windows) return end + local current_buf = vim.api.nvim_win_get_buf(windows.output_win) + local buffer_was_replaced = current_buf ~= windows.output_buf + output_window.restore_winfix_options(windows.output_win) if windows.position == 'current' then - if state.current_code_buf and vim.api.nvim_buf_is_valid(state.current_code_buf) then + if not buffer_was_replaced and state.current_code_buf and vim.api.nvim_buf_is_valid(state.current_code_buf) then pcall(vim.api.nvim_win_set_buf, windows.output_win, state.current_code_buf) end if state.saved_window_options then @@ -128,6 +131,10 @@ local function close_or_restore_output_window(windows) return end + if buffer_was_replaced then + return + end + pcall(vim.api.nvim_win_close, windows.output_win, true) end From c7eec83e120eaef3a8c623b45c7f0a2e346e2a09 Mon Sep 17 00:00:00 2001 From: Julio Garcia Date: Sun, 26 Jul 2026 14:07:58 +0200 Subject: [PATCH 2/3] fix: guard WinClosed and map o in both opencode windows Only teardown opencode UI when the output window itself closes, not when input/footer closes. Add o mapping to both output and input buffers using buffer-ID comparison to avoid nil-table issues in the window-ID approach. --- lua/opencode/ui/autocmds.lua | 2 +- lua/opencode/ui/input_window.lua | 11 +++++++++++ lua/opencode/ui/output_window.lua | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lua/opencode/ui/autocmds.lua b/lua/opencode/ui/autocmds.lua index 9c32bdb7..adc3f4f9 100644 --- a/lua/opencode/ui/autocmds.lua +++ b/lua/opencode/ui/autocmds.lua @@ -19,7 +19,7 @@ function M.setup_autocmds(windows) end local closed_win = tonumber(opts.match) - if vim.tbl_contains(wins, closed_win) then + if vim.tbl_contains(wins, closed_win) and closed_win == windows.output_win then vim.schedule(function() require('opencode.ui.ui').teardown_visible_windows(windows) end) diff --git a/lua/opencode/ui/input_window.lua b/lua/opencode/ui/input_window.lua index cec511a4..e5c64fd8 100644 --- a/lua/opencode/ui/input_window.lua +++ b/lua/opencode/ui/input_window.lua @@ -535,6 +535,17 @@ function M.setup_keymaps(windows) local keymap = require('opencode.keymap') keymap.setup_window_keymaps(config.keymap.input_window, windows.input_buf) + + vim.keymap.set('n', 'o', function() + for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do + if vim.api.nvim_win_is_valid(win) then + local buf = vim.api.nvim_win_get_buf(win) + if buf ~= windows.output_buf and buf ~= windows.input_buf and buf ~= (windows.footer_buf or -1) then + pcall(vim.api.nvim_win_close, win, true) + end + end + end + end, { buffer = windows.input_buf }) end function M.setup_autocmds(windows, group) diff --git a/lua/opencode/ui/output_window.lua b/lua/opencode/ui/output_window.lua index 16ba4f4e..08cd356e 100644 --- a/lua/opencode/ui/output_window.lua +++ b/lua/opencode/ui/output_window.lua @@ -700,6 +700,17 @@ function M.setup_keymaps(windows, preserve_existing) vim.api.nvim_win_set_cursor(0, { 1, 0 }) end, { buffer = windows.output_buf }) end + + vim.keymap.set('n', 'o', function() + for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do + if vim.api.nvim_win_is_valid(win) then + local buf = vim.api.nvim_win_get_buf(win) + if buf ~= windows.output_buf and buf ~= windows.input_buf and buf ~= (windows.footer_buf or -1) then + pcall(vim.api.nvim_win_close, win, true) + end + end + end + end, { buffer = windows.output_buf }) end ---@param windows OpencodeWindowState From 5757c1da2944160eba8a6a21077332eda356e335 Mon Sep 17 00:00:00 2001 From: Julio Garcia Date: Sun, 26 Jul 2026 14:18:47 +0200 Subject: [PATCH 3/3] test: bump flaky inline_input timeout on macOS CI Increase vim.wait timeout from 50ms to 100ms for the multiline initial_text cursor position assertion, which is prone to timing out on macos-latest with Neovim 0.11.4. --- tests/unit/inline_input_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/inline_input_spec.lua b/tests/unit/inline_input_spec.lua index 71de88e8..25bc0b61 100644 --- a/tests/unit/inline_input_spec.lua +++ b/tests/unit/inline_input_spec.lua @@ -144,7 +144,7 @@ describe('inline_input', function() on_submit = function() end, on_cancel = function() end, }) - assert.is_true(vim.wait(50, function() + assert.is_true(vim.wait(100, function() return vim.api.nvim_get_current_win() == input.win and vim.deep_equal(vim.api.nvim_win_get_cursor(input.win), { 2, #'second line' }) end))