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
18 changes: 17 additions & 1 deletion lua/opencode/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,29 @@ function M.paste_image()
core.paste_image_from_clipboard()
end

--- Check if opencode windows are in the current tab page
--- @return boolean
local function are_windows_in_current_tab()
if not state.windows or not state.windows.output_win then
return false
end

local current_tab = vim.api.nvim_get_current_tabpage()
local ok, win_tab = pcall(vim.api.nvim_win_get_tabpage, state.windows.output_win)
return ok and win_tab == current_tab
end

M.toggle = Promise.async(function(new_session)
-- When auto_hide input is enabled, always focus input; otherwise use last focused
local focus = 'input' ---@cast focus 'input' | 'output'
if not config.ui.input.auto_hide then
focus = state.last_focused_opencode_window or 'input'
end
if state.windows == nil then

if state.windows == nil or not are_windows_in_current_tab() then
if state.windows then
M.close()
end
core.open({ new_session = new_session == true, focus = focus, start_insert = false }):await()
else
M.close()
Expand Down
6 changes: 3 additions & 3 deletions lua/opencode/ui/autocmds.lua
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,18 @@ function M.setup_autocmds(windows)
end
end

---@param windows OpencodeWindowState?
function M.setup_resize_handler(windows)
local resize_group = vim.api.nvim_create_augroup('OpencodeResize', { clear = true })
vim.api.nvim_create_autocmd('VimResized', {
group = resize_group,
callback = function()
require('opencode.ui.topbar').render()
require('opencode.ui.footer').update_window(windows)
require('opencode.ui.input_window').update_dimensions(windows)
require('opencode.ui.output_window').update_dimensions(windows)
input_window.update_dimensions(windows)
output_window.update_dimensions(windows)
end,
})

vim.api.nvim_create_autocmd('WinResized', {
group = resize_group,
callback = function(args)
Expand Down
76 changes: 76 additions & 0 deletions lua/opencode/ui/buf_fix_win.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
--- Prevents a buffer from appearing in multiple windows (opposite of 'winfixbuf')
---
--- This module solves the problem where buffers can appear in multiple windows
--- despite having 'winfixbuf' set. The 'winfixbuf' option prevents a window from
--- changing buffers, but doesn't prevent a buffer from appearing in multiple windows.
---
local M = {}
local buff_to_win_map = {}
local global_autocmd_setup = false

local function close_duplicates(buf, get_win)
local intended = get_win()
if not intended or not vim.api.nvim_win_is_valid(intended) then
return
end

local wins = vim.fn.win_findbuf(buf)
if #wins > 1 then
vim.schedule(function()
for _, win in ipairs(wins) do
if win ~= intended and vim.api.nvim_win_is_valid(win) then
pcall(vim.api.nvim_win_close, win, true)
end
end
end)
end
end

local check_all_buffers = vim.schedule_wrap(function()
for buf, get_win in pairs(buff_to_win_map) do
if vim.api.nvim_buf_is_valid(buf) then
close_duplicates(buf, get_win)
else
buff_to_win_map[buf] = nil
end
end
end)

local function setup()
if global_autocmd_setup then
return
end
global_autocmd_setup = true

local augroup = vim.api.nvim_create_augroup('OpenCodeBufFixWin', { clear = false })
vim.api.nvim_create_autocmd({ 'WinNew', 'VimResized' }, {
group = augroup,
callback = check_all_buffers,
})
vim.api.nvim_create_autocmd('BufDelete', {
callback = function(args)
if args and args.buf then
buff_to_win_map[args.buf] = nil
end
end,
})
end

--- Protect a buffer from appearing in multiple windows
---@param buf integer Buffer number
---@param get_intended_window fun(): integer? Function returning intended window ID
function M.fix_to_win(buf, get_intended_window)
setup()

buff_to_win_map[buf] = get_intended_window
local augroup = vim.api.nvim_create_augroup('OpenCodeBufFixWin_' .. buf, { clear = true })
vim.api.nvim_create_autocmd('BufWinEnter', {
group = augroup,
buffer = buf,
callback = function()
close_duplicates(buf, get_intended_window)
end,
})
end

return M
8 changes: 8 additions & 0 deletions lua/opencode/ui/input_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ end
function M.create_buf()
local input_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_option_value('filetype', 'opencode', { buf = input_buf })

local buffixwin = require('opencode.ui.buf_fix_win')
buffixwin.fix_to_win(input_buf, function()
return state.windows and state.windows.input_win
end)

return input_buf
end

Expand Down Expand Up @@ -261,6 +267,8 @@ function M.setup(windows)
set_win_option('number', false, windows)
set_win_option('relativenumber', false, windows)
set_buf_option('buftype', 'nofile', windows)
set_buf_option('bufhidden', 'hide', windows)
set_buf_option('buflisted', false, windows)
set_buf_option('swapfile', false, windows)

if config.ui.position ~= 'current' then
Expand Down
10 changes: 10 additions & 0 deletions lua/opencode/ui/output_window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ M.viewport_at_bottom = true
function M.create_buf()
local output_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_set_option_value('filetype', 'opencode_output', { buf = output_buf })

local buffixwin = require('opencode.ui.buf_fix_win')
buffixwin.fix_to_win(output_buf, function()
return state.windows and state.windows.output_win
end)

return output_buf
end

Expand All @@ -23,6 +29,7 @@ function M._build_output_win_config()
}
end

---@param windows OpencodeWindowState?
function M.mounted(windows)
windows = windows or state.windows
return windows and windows.output_buf and windows.output_win and vim.api.nvim_win_is_valid(windows.output_win)
Expand Down Expand Up @@ -96,7 +103,10 @@ function M.setup(windows)
set_win_option('relativenumber', false, windows.output_win)
set_buf_option('modifiable', false, windows.output_buf)
set_buf_option('buftype', 'nofile', windows.output_buf)
set_buf_option('bufhidden', 'hide', windows.output_buf)
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
Expand Down
Loading