Skip to content

fix(ui): prevent duplicate windows#233

Closed
disrupted wants to merge 1 commit intosudo-tee:mainfrom
disrupted:vimresized-duplicate-input-wins
Closed

fix(ui): prevent duplicate windows#233
disrupted wants to merge 1 commit intosudo-tee:mainfrom
disrupted:vimresized-duplicate-input-wins

Conversation

@disrupted
Copy link
Contributor

@disrupted disrupted commented Feb 3, 2026

fix #144
not entirely sure if this approach aligns with best-practices
at least I am no longer seeing the issue on my end

@sudo-tee
Copy link
Owner

sudo-tee commented Feb 4, 2026

I played a little bit wit it.
It seems to work when resizingf but it does not fix the root cause.

I am still able to duplicate the input window if I wun :tabdo wincmd = when focused in the input.

I will play a little bit with this to see if there is a better solution

@sudo-tee
Copy link
Owner

sudo-tee commented Feb 4, 2026

How about a more generic approach with a utility for fix a buf to a window, it's like the opposite of winfixbuf.

I came up with the following based on your reconcile_windows

--- 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 then
    return
  end

  local wins = vim.fn.win_findbuf(buf)
  if #wins <= 1 then
    return
  end

  for _, win in ipairs(wins) do
    if win ~= intended and vim.api.nvim_win_is_valid(win) then
      vim.schedule(function()
        pcall(vim.api.nvim_win_close, win, true)
      end)
    end
  end
end

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

  vim.api.nvim_create_autocmd('WinNew', {
    callback = function()
      vim.schedule(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)
    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
  vim.api.nvim_create_autocmd('BufWinEnter', {
    buffer = buf,
    callback = function()
      close_duplicates(buf, get_intended_window)
    end,
  })
end

return M

And it could be used like so on a buffer after creation

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

In my quick tests it covers,

resizing, tabdo wincmd=, vsplit and split from messing the layout.

@disrupted
Copy link
Contributor Author

great, clearly this is a much more robust solution! feel free to open a PR to supersede mine.

overall I haven't fully understand why this issue occurs in the first place. it feels like we're doing something fundamentally wrong that makes these workarounds necessary. but I am happy about any pragmatic solution at this point. we can always refine it later once we identify the root cause of this problem

@sudo-tee
Copy link
Owner

sudo-tee commented Feb 4, 2026

great, clearly this is a much more robust solution! feel free to open a PR to supersede mine.

overall I haven't fully understand why this issue occurs in the first place. it feels like we're doing something fundamentally wrong that makes these workarounds necessary. but I am happy about any pragmatic solution at this point. we can always refine it later once we identify the root cause of this problem

I have tested other similar plugins and most of them have similar issues . you can split , duplicate windows. I think it's more of an issue related to fixed window layout, some plugins like https://github.com/stevearc/stickybuf.nvim uses a similar approach.

I will create a PR to use my fix

Thanks

@sudo-tee
Copy link
Owner

sudo-tee commented Feb 5, 2026

Closing this one in favor of
#238

Which was inspired by the work on this branch

@sudo-tee sudo-tee closed this Feb 5, 2026
@disrupted disrupted deleted the vimresized-duplicate-input-wins branch February 5, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Got two input buffers when I resize the nvim window

2 participants