Skip to content
Open
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
48 changes: 47 additions & 1 deletion lua/opencode/state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
---@field clear_hidden_window_state fun()
---@field has_hidden_buffers fun(): boolean
---@field consume_hidden_buffers fun(): OpencodeHiddenBuffers|nil
---@field resolve_toggle_decision fun(persist_state: boolean, has_display_route: boolean): OpencodeToggleDecision
---@field are_opencode_only_windows fun(): boolean
---@field resolve_toggle_decision fun(persist_state: boolean, has_display_route: boolean): OpencodeToggleDecision
---@field resolve_open_windows_action fun(): 'reuse_visible'|'restore_hidden'|'create_fresh'
---@field get_window_cursor fun(win_id: integer|nil): integer[]|nil

Expand Down Expand Up @@ -264,6 +265,43 @@ function M.are_windows_in_current_tab()
or M.is_window_in_current_tab(_state.windows.output_win)
end

--- Returns true when every normal (non-floating) window in the current tab
--- belongs to opencode (i.e. there are no code windows open alongside it).
---@return boolean
function M.are_opencode_only_windows()
local w = _state.windows
if not w then
return false
end

local opencode_wins = {}
if w.input_win and vim.api.nvim_win_is_valid(w.input_win) then
opencode_wins[w.input_win] = true
end
if w.output_win and vim.api.nvim_win_is_valid(w.output_win) then
opencode_wins[w.output_win] = true
end
if w.footer_win and vim.api.nvim_win_is_valid(w.footer_win) then
opencode_wins[w.footer_win] = true
end

-- No opencode windows tracked → not an only-opencode situation
if vim.tbl_isempty(opencode_wins) then
return false
end

local current_tab = vim.api.nvim_get_current_tabpage()
for _, win_id in ipairs(vim.api.nvim_tabpage_list_wins(current_tab)) do
local cfg = vim.api.nvim_win_get_config(win_id)
-- Skip floating windows
if cfg.relative == '' and not opencode_wins[win_id] then
return false
end
end

return true
end

---@return boolean
function M.is_visible()
return M.get_window_state().status == 'visible'
Expand All @@ -274,6 +312,7 @@ end
---@field in_tab boolean
---@field persist_state boolean
---@field has_display_route boolean
---@field only_windows boolean

---@generic T
---@param rules T[]
Expand Down Expand Up @@ -323,6 +362,12 @@ local TOGGLE_ACTION_RULES = {
return ctx.status == 'visible' and ctx.in_tab and not ctx.persist_state
end,
},
{
action = 'hide',
when = function(ctx)
return ctx.status == 'visible' and ctx.in_tab and ctx.only_windows and not ctx.has_display_route
end,
},
{
action = 'hide',
when = function(ctx)
Expand All @@ -348,6 +393,7 @@ local function lookup_toggle_action(status, in_tab, persist_state, has_display_r
in_tab = in_tab,
persist_state = persist_state,
has_display_route = has_display_route,
only_windows = M.are_opencode_only_windows(),
}

local matched_rule = first_matching_rule(TOGGLE_ACTION_RULES, function(rule)
Expand Down
40 changes: 40 additions & 0 deletions lua/opencode/ui/autocmds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,46 @@ function M.setup_autocmds(windows)
end,
})
end

M.setup_window_only_keymap(windows)
end

--- Set <C-w>o / <C-w><C-o> on both opencode buffers to close all other
--- non-floating windows while keeping the opencode pair intact.
--- Also clears the saved width ratio so the next open uses the config default.
---@param windows OpencodeWindowState
function M.setup_window_only_keymap(windows)
local opencode_wins = function()
local t = {}
for _, w in ipairs({ windows.input_win, windows.output_win, windows.footer_win }) do
if w then
t[w] = true
end
end
return t
end

local handler = function()
local keep = opencode_wins()
keep[vim.api.nvim_get_current_win()] = true
for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
if not keep[win] and vim.api.nvim_win_is_valid(win) then
local cfg = vim.api.nvim_win_get_config(win)
if cfg.relative == '' then
pcall(vim.api.nvim_win_close, win, false)
end
end
end
-- Don't remember the current opencode width; next open will use config default
require('opencode.state').last_window_width_ratio = nil
end

for _, buf in ipairs({ windows.input_buf, windows.output_buf }) do
if buf and vim.api.nvim_buf_is_valid(buf) then
vim.keymap.set('n', '<C-w>o', handler, { buffer = buf, desc = 'Keep only opencode windows', nowait = true })
vim.keymap.set('n', '<C-w><C-o>', handler, { buffer = buf, desc = 'Keep only opencode windows', nowait = true })
end
end
end

---@param windows OpencodeWindowState?
Expand Down
6 changes: 3 additions & 3 deletions lua/opencode/ui/input_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,9 @@ function M.setup(windows)
set_buf_option('buflisted', false, windows)
set_buf_option('swapfile', false, windows)

if config.ui.position ~= 'current' then
set_win_option('winfixbuf', true, windows)
end
require('opencode.ui.buf_fix_win').fix_to_win(windows.input_buf, function()
return state.windows and state.windows.input_win
end)
set_win_option('winfixwidth', true, windows)

M.update_dimensions(windows)
Expand Down
6 changes: 3 additions & 3 deletions lua/opencode/ui/output_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ function M.setup(windows)
set_buf_option('buflisted', false, windows.output_buf)
set_buf_option('swapfile', false, windows.output_buf)

if config.ui.position ~= 'current' then
set_win_option('winfixbuf', true, windows.output_win)
end
require('opencode.ui.buf_fix_win').fix_to_win(windows.output_buf, function()
return state.windows and state.windows.output_win
end)
set_win_option('winfixheight', true, windows.output_win)
set_win_option('winfixwidth', true, windows.output_win)
set_win_option('signcolumn', 'yes', windows.output_win)
Expand Down
6 changes: 0 additions & 6 deletions lua/opencode/ui/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ end
local function close_or_restore_output_window(windows)
if config.ui.position == 'current' then
if windows.output_win and vim.api.nvim_win_is_valid(windows.output_win) then
pcall(vim.api.nvim_set_option_value, 'winfixbuf', false, { win = windows.output_win })
if 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
Expand All @@ -135,7 +134,6 @@ function M.hide_visible_windows(windows)

local snapshot = capture_hidden_snapshot(windows)

-- Only save width ratio for split modes (not dialog/current mode)
if config.ui.position ~= 'current' then
local total_cols = vim.o.columns
local current_width = vim.api.nvim_win_get_width(windows.output_win)
Expand Down Expand Up @@ -329,10 +327,6 @@ function M.create_split_windows(input_buf, output_buf)
local input_win = open_split(ui_conf.input_position, 'horizontal')
local output_win = main_win

if ui_conf.position == 'current' then
pcall(vim.api.nvim_set_option_value, 'winfixbuf', false, { win = output_win })
end

vim.api.nvim_win_set_buf(input_win, input_buf)
vim.api.nvim_win_set_buf(output_win, output_buf)
return { input_win = input_win, output_win = output_win }
Expand Down
Loading