Skip to content

Commit faaee41

Browse files
committed
fix: layout shift
1 parent ca33ba7 commit faaee41

2 files changed

Lines changed: 363 additions & 77 deletions

File tree

lua/zen/init.lua

Lines changed: 104 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ local opts = {
2727
bottom = {},
2828
left = { { filetype = "*", min_width = 46 } },
2929
}
30+
--- @type table<number, { left: number, right: number }>
3031
local state = {
31-
[vim.api.nvim_get_current_tabpage()] = { left = nil, right = nil },
32+
[vim.api.nvim_get_current_tabpage()] = { left = -1, right = -1 },
3233
}
3334

3435
local function get_main_width()
@@ -77,18 +78,19 @@ local function is_filetype(target, filetype)
7778
return false
7879
end
7980

80-
---@param position "left" | "right"
81+
---@param position "top" | "right" | "bottom" | "left"
8182
---@param filetype string
8283
---@return number
8384
local function get_min_width(position, filetype)
8485
local wildcard_width = 0
8586
for _, integration in ipairs(opts[position]) do
8687
if type(integration) == "table" then
87-
if integration.min_width and integration.filetype ~= "*" and is_filetype(filetype, integration.filetype) then
88-
return integration.min_width
88+
local min_w = integration.min_width or 0
89+
if min_w > 0 and integration.filetype ~= "*" and is_filetype(filetype, integration.filetype) then
90+
return min_w
8991
end
90-
if integration.filetype == "*" and integration.min_width then
91-
wildcard_width = integration.min_width
92+
if integration.filetype == "*" and min_w > 0 then
93+
wildcard_width = min_w
9294
end
9395
end
9496
end
@@ -130,6 +132,18 @@ local function filetypes_visible(filetypes)
130132
return false
131133
end
132134

135+
---@param list string[]
136+
---@param filetype Filetype
137+
local function append_filetype(list, filetype)
138+
if type(filetype) == "table" then
139+
for _, ft in ipairs(filetype) do
140+
table.insert(list, ft)
141+
end
142+
else
143+
table.insert(list, filetype)
144+
end
145+
end
146+
133147
---@param filetypes string[]
134148
---@param type_to_remove string
135149
local function remove_file_type(filetypes, type_to_remove)
@@ -150,8 +164,7 @@ local function is_buff_integration(buf)
150164
end
151165
for _, position in ipairs({ "left", "right", "top", "bottom" }) do
152166
for _, integration in ipairs(opts[position] or {}) do
153-
---@diagnostic disable-next-line: undefined-field
154-
if type(integration) == "table" and is_filetype(filetype, integration.filetype) then
167+
if type(integration) == "table" and is_filetype(filetype, integration.filetype) then
155168
return true
156169
end
157170
end
@@ -195,14 +208,6 @@ local function get_vsplits()
195208
return vsplits
196209
end
197210

198-
---@return boolean
199-
local function is_hsplit(buf)
200-
local win_id = vim.fn.bufwinid(buf)
201-
local width = vim.api.nvim_win_get_width(win_id)
202-
local height = vim.api.nvim_win_get_height(win_id)
203-
return width > height
204-
end
205-
206211
---@param position "top" | "right" | "bottom" | "left"
207212
---@return boolean
208213
local function is_integration_open(position)
@@ -232,11 +237,55 @@ local function get_window_by_filetype(filetype)
232237
return nil
233238
end
234239

235-
local function adjust_top_bottom_window_hack(target_window, position)
236-
if target_window then
237-
vim.api.nvim_win_call(target_window, function()
238-
vim.cmd("wincmd " .. position)
239-
end)
240+
local _saved_position_heights = {}
241+
local _in_handler = false
242+
243+
local function save_top_bottom_heights()
244+
for _, position in ipairs({ "top", "bottom" }) do
245+
for _, integration in pairs(opts[position]) do
246+
if type(integration) == "table" then
247+
local win = get_window_by_filetype(integration.filetype)
248+
if win then
249+
local w = vim.api.nvim_win_get_width(win)
250+
-- only save when integration spans full width (properly positioned)
251+
if w == vim.o.columns then
252+
_saved_position_heights[position] = vim.api.nvim_win_get_height(win)
253+
end
254+
break
255+
end
256+
end
257+
end
258+
end
259+
end
260+
261+
local function reposition_top_bottom_integrations()
262+
for _, integration in pairs(opts.top) do
263+
local win = get_window_by_filetype(integration.filetype)
264+
if win then
265+
local needs_reposition = vim.api.nvim_win_get_width(win) ~= vim.o.columns
266+
if needs_reposition then
267+
local height_before = vim.api.nvim_win_get_height(win)
268+
vim.api.nvim_win_set_config(win, { split = "above", win = -1 })
269+
vim.api.nvim_win_set_height(win, _saved_position_heights.top or height_before)
270+
elseif _saved_position_heights.top then
271+
vim.api.nvim_win_set_height(win, _saved_position_heights.top)
272+
end
273+
_saved_position_heights.top = vim.api.nvim_win_get_height(win)
274+
end
275+
end
276+
for _, integration in pairs(opts.bottom) do
277+
local win = get_window_by_filetype(integration.filetype)
278+
if win then
279+
local needs_reposition = vim.api.nvim_win_get_width(win) ~= vim.o.columns
280+
if needs_reposition then
281+
local height_before = vim.api.nvim_win_get_height(win)
282+
vim.api.nvim_win_set_config(win, { split = "below", win = -1 })
283+
vim.api.nvim_win_set_height(win, _saved_position_heights.bottom or height_before)
284+
elseif _saved_position_heights.bottom then
285+
vim.api.nvim_win_set_height(win, _saved_position_heights.bottom)
286+
end
287+
_saved_position_heights.bottom = vim.api.nvim_win_get_height(win)
288+
end
240289
end
241290
end
242291

@@ -271,28 +320,6 @@ local function setup(options)
271320
---@type ConfigOptions
272321
opts = vim.tbl_extend("force", opts, options or {})
273322

274-
vim.api.nvim_create_autocmd("CursorMoved", {
275-
-- TODO: use pattern for better perf
276-
callback = function(args)
277-
if is_buff_integration(args.buf) then
278-
local buf_info = vim.fn.getbufinfo(args.buf)
279-
280-
local filetype = vim.api.nvim_get_option_value("filetype", { buf = args.buf })
281-
for _, position in ipairs({ "right", "left" }) do
282-
for _, integration in pairs(opts[position]) do
283-
---@diagnostic disable-next-line: undefined-field
284-
if type(integration) == "table" and is_filetype(filetype, integration.filetype) then
285-
local new_width = math.max(get_min_width(position, filetype), math.floor((vim.o.columns - get_main_width()) / 2))
286-
vim.api.nvim_win_set_width(buf_info[1].windows[1], new_width)
287-
return
288-
end
289-
end
290-
end
291-
end
292-
end,
293-
desc = "HACK: adjust the integration when opening",
294-
})
295-
296323
vim.api.nvim_create_autocmd({ "VimEnter", "TabNew" }, {
297324
callback = function()
298325
-- disable when window is too small
@@ -317,9 +344,10 @@ local function setup(options)
317344
callback = function()
318345
if vim.bo.filetype == "zen-left" then
319346
vim.cmd("wincmd l")
320-
end
321-
if vim.bo.filetype == "zen-right" then
347+
elseif vim.bo.filetype == "zen-right" then
322348
vim.cmd("wincmd h")
349+
else
350+
save_top_bottom_heights()
323351
end
324352
end,
325353
desc = "Prevent the cursor from moving to the side buffers.",
@@ -419,25 +447,37 @@ local function setup(options)
419447
return
420448
end
421449

422-
local left_file_types = { "fugitiveblame", "fyler", "undotree", "dbui", "zen-left" }
450+
-- save top/bottom integration heights before recreating zen buffers
451+
if not _in_handler then
452+
save_top_bottom_heights()
453+
end
454+
455+
local left_file_types = { "zen-left" }
456+
for _, integration in ipairs(opts.left) do
457+
if type(integration) == "table" and integration.filetype ~= "*" then
458+
append_filetype(left_file_types, integration.filetype)
459+
end
460+
end
423461
remove_file_type(left_file_types, file_type)
424462
if not filetypes_visible(left_file_types) then
425463
state[vim.api.nvim_get_current_tabpage()].left = create_window("left")
426464
vim.cmd("wincmd l")
427465
end
428466

429-
local right_file_types = { "dapui_scopes", "neotest-summary", "zen-right" }
467+
local right_file_types = { "zen-right" }
468+
for _, integration in ipairs(opts.right) do
469+
if type(integration) == "table" and integration.filetype ~= "*" then
470+
append_filetype(right_file_types, integration.filetype)
471+
end
472+
end
430473
remove_file_type(right_file_types, file_type)
431474
if not filetypes_visible(right_file_types) then
432475
state[vim.api.nvim_get_current_tabpage()].right = create_window("right")
433476
vim.cmd("wincmd h")
434477
end
435478

436-
for _, integration in pairs(opts.top) do
437-
adjust_top_bottom_window_hack(get_window_by_filetype(integration.filetype), "K")
438-
end
439-
for _, integration in pairs(opts.bottom) do
440-
adjust_top_bottom_window_hack(get_window_by_filetype(integration.filetype), "J")
479+
if not _in_handler then
480+
reposition_top_bottom_integrations()
441481
end
442482
resize_side_buffers()
443483
end,
@@ -464,11 +504,15 @@ local function setup(options)
464504
return
465505
end
466506

467-
for _, position in ipairs({ "top", "right", "bottom", "left" }) do
507+
_in_handler = true
508+
509+
---@type ("top"|"right"|"bottom"|"left")[]
510+
local positions = { "top", "right", "bottom", "left" }
511+
for _, position in ipairs(positions) do
468512
for _, integration in pairs(opts[position]) do
469513
if type(integration) == "table" and is_filetype(filetype, integration.filetype) then
470514
close_side_buffer(position)
471-
for _, position_inner in ipairs({ "top", "right", "bottom", "left" }) do
515+
for _, position_inner in ipairs(positions) do
472516
for _, integration_inner in pairs(opts[position_inner]) do
473517
if
474518
position_inner == position
@@ -481,32 +525,19 @@ local function setup(options)
481525
end
482526

483527
if position == "left" or position == "right" then
484-
local min_width = get_min_width(position, filetype)
485-
if min_width > 0 then
486-
local win = vim.fn.bufwinid(args.buf)
487-
if win ~= -1 then
488-
local current_width = vim.api.nvim_win_get_width(win)
489-
if current_width < min_width then
490-
vim.api.nvim_win_set_width(win, min_width)
491-
end
492-
end
528+
local new_width = math.max(get_min_width(position, filetype), math.floor((vim.o.columns - get_main_width()) / 2))
529+
local win = vim.fn.bufwinid(args.buf)
530+
if win ~= -1 then
531+
vim.api.nvim_win_set_width(win, new_width)
493532
end
494533
end
495534
end
496535
end
497536
end
498537

499-
for _, integration in pairs(opts.top) do
500-
if not is_hsplit(args.buf) then
501-
adjust_top_bottom_window_hack(get_window_by_filetype(integration.filetype), "K")
502-
end
503-
end
504-
for _, integration in pairs(opts.bottom) do
505-
if not is_hsplit(args.buf) then
506-
adjust_top_bottom_window_hack(get_window_by_filetype(integration.filetype), "J")
507-
end
508-
end
538+
reposition_top_bottom_integrations()
509539
resize_side_buffers()
540+
_in_handler = false
510541
end,
511542
desc = "Close side buffer plugins if another plugin is already occupying that side.",
512543
})

0 commit comments

Comments
 (0)