From 6d74bb45eb89aca2a6387b79cc623256fe2dd18d Mon Sep 17 00:00:00 2001 From: shortcuts Date: Wed, 3 Jul 2024 01:49:05 +0200 Subject: [PATCH] fix: inconsistent walking --- lua/no-neck-pain/main.lua | 2 +- lua/no-neck-pain/state.lua | 68 +++++++++++++++++++------------------- lua/no-neck-pain/wins.lua | 31 +++++++++++++---- tests/test_API.lua | 8 +++-- 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/lua/no-neck-pain/main.lua b/lua/no-neck-pain/main.lua index b966462..cc1fcfc 100644 --- a/lua/no-neck-pain/main.lua +++ b/lua/no-neck-pain/main.lua @@ -248,7 +248,7 @@ function N.enable(scope) return D.log(p.event, "skip") end - S.refreshVSplits(S, scope) + S.refreshVSplits(S, p.event) if S.wantsSides(S) and S.checkSides(S, "and", false) then return D.log(p.event, "no side buffer") diff --git a/lua/no-neck-pain/state.lua b/lua/no-neck-pain/state.lua index 0c9a75b..44ee4c3 100644 --- a/lua/no-neck-pain/state.lua +++ b/lua/no-neck-pain/state.lua @@ -20,19 +20,20 @@ function State:save() _G.NoNeckPain.state = self end ----Sets the vsplits counter to 1. +---Initializes the vsplits to an empty table. --- ---@private function State:initVSplits() - self.tabs[self.activeTab].wins.vsplits = 0 + self.tabs[self.activeTab].wins.vsplits = {} end ---Gets the tab vsplits counter. --- ----@return number: the number of active vsplits. +---@return table: the vsplits window IDs. +---@return number: the number of vsplits. ---@private function State:getVSplits() - return self.tabs[self.activeTab].wins.vsplits + return self.tabs[self.activeTab].wins.vsplits, A.length(self.tabs[self.activeTab].wins.vsplits) end ---Whether the side is enabled in the config or not. @@ -489,7 +490,7 @@ function State:setTab(id) id = id, scratchPadEnabled = false, wins = { - vsplits = 0, + vsplits = {}, main = { curr = nil, left = nil, @@ -501,12 +502,17 @@ function State:setTab(id) self.activeTab = id end ----Increases the vsplits counter. +---Insert the given ids window ids in the state. --- ----@param nb number: the number of columns in the given row. +---@param vsplits table: the vsplits leafs to add to the state. ---@private -function State:increaseVSplits(nb) - self.tabs[self.activeTab].wins.vsplits = self.tabs[self.activeTab].wins.vsplits + nb +function State:insertVSplits(vsplits) + for _, vsplit in ipairs(vsplits) do + -- only add the leaf because we can receive something like { "col" {...}}, which will then be walked in and inserted later + if vsplit[1] == "leaf" then + self.tabs[self.activeTab].wins.vsplits[vsplit[2]] = true + end + end end ---Recursively walks in the `winlayout` until it has computed every column present in the UI. @@ -517,51 +523,45 @@ end ---that depth from the number of elements in the current `row` in order to avoid counting all parents many times. --- ---@private -function State:walkLayout(depth, vsplit, curr) +function State:walkLayout(parent, curr) for _, group in ipairs(curr) do - -- a row indicates a `vsplit` window container - if type(group) == "string" and group == "row" then - vsplit = true - elseif type(group) == "table" then - local len = #group - if vsplit then - -- even if we are super deep in the field, len minimal value is always 1. - if len <= depth then - len = depth + 1 - end - + if type(group) == "table" then + -- a row indicates a `vsplit` window container + if parent == "row" then -- we remove the depth from the len in order to avoid counting parents multiple times. - self.increaseVSplits(self, len - depth) - - -- reset vsplit as this layer as been computed already, increase depth as we will dug again. - depth = depth + 1 - vsplit = false + self.insertVSplits(self, group) + parent = nil end - self.walkLayout(self, depth, vsplit, group) + self.walkLayout(self, parent, group) -- depth 0 on a leaf is only possible after enabling nnp as there's nothing in the layout other than the main window - elseif depth == 0 and group == "leaf" then - self.increaseVSplits(self, 1) + elseif group == "leaf" and parent == nil then + self.insertVSplits(self, { curr }) + elseif type(group) == "string" then + parent = group end end end ----Refresh vsplits counter based on the `winlayout`. +---Refresh vsplits state based on the `winlayout`. --- ---@param scope string: the caller of the method. ---@return boolean: whether the number of vsplits changed or not. ---@private function State:refreshVSplits(scope) - local currentVSplits = self.getVSplits(self) + local _, nbVSplits = self.getVSplits(self) self.initVSplits(self) - self.walkLayout(self, 0, false, vim.fn.winlayout(self.activeTab)) + self.walkLayout(self, nil, vim.fn.winlayout(self.activeTab)) - D.log(scope, "computed %d vsplits", self.getVSplits(self)) + D.log(scope, "computed %d vsplits", nbVSplits) self.save(self) - return currentVSplits ~= self.getVSplits(self) + local _, nbNBVsplits = self.getVSplits(self) + + -- TODO: real table diff + return nbVSplits ~= nbNBVsplits end return State diff --git a/lua/no-neck-pain/wins.lua b/lua/no-neck-pain/wins.lua index 2b5c3bf..99e913a 100644 --- a/lua/no-neck-pain/wins.lua +++ b/lua/no-neck-pain/wins.lua @@ -10,7 +10,7 @@ local W = {} --- ---@param id number: the id of the window. ---@param width number: the width to apply to the window. ----@param side "left"|"right"|"curr": the side of the window being resized, used for logging only. +---@param side "left"|"right"|"curr"|"vsplit": the side of the window being resized, used for logging only. ---@private local function resize(id, width, side) D.log(side, "resizing %d with padding %d", id, width) @@ -177,6 +177,27 @@ function W.createSideBuffers(skipIntegrations) end end + local vsplits, nbVSplits = S.getVSplits(S) + local leftID = S.getSideID(S, "left") + local rightID = S.getSideID(S, "right") + + -- if we still have side buffers open at this point, and we have vsplit opened, + -- there might be width issues so we the resize opened vsplits. + if (leftID or rightID) and nbVSplits > 0 then + local sWidth = wins.left.padding or wins.right.padding + local nbSide = leftID and rightID and 2 or 1 + + -- get the available usable width (screen size without side paddings) + sWidth = vim.api.nvim_list_uis()[1].width - sWidth * nbSide + sWidth = math.floor(sWidth / (nbVSplits - nbSide)) + + for vsplit, _ in pairs(vsplits) do + if vsplit ~= leftID and vsplit ~= rightID and vsplit ~= S.getSideID(S, "curr") then + resize(vsplit, sWidth, "vsplit") + end + end + end + -- closing integrations and reopening them means new window IDs if closedIntegrations then S.refreshIntegrations(S, "createSideBuffers") @@ -208,18 +229,14 @@ function W.getPadding(side) return 0 end - local columns = S.getVSplits(S) + local _, columns = S.getVSplits(S) for _, s in ipairs(Co.SIDES) do - if S.isSideEnabled(S, s) then + if S.isSideEnabled(S, s) and columns > 1 then columns = columns - 1 end end - if columns < 1 then - columns = 1 - end - -- we need to see if there's enough space left to have side buffers local occupied = _G.NoNeckPain.config.width * columns diff --git a/tests/test_API.lua b/tests/test_API.lua index 30a78dd..e5a5b91 100644 --- a/tests/test_API.lua +++ b/tests/test_API.lua @@ -242,7 +242,7 @@ T["enable"]["(single tab) sets state"] = function() right = 1002, }) - Helpers.expect.state(child, "tabs[1].wins.vsplits", 1) + Helpers.expect.state(child, "tabs[1].wins.vsplits[1000]", true) Helpers.expect.state_type(child, "tabs[1].wins.integrations", "table") @@ -270,7 +270,7 @@ T["enable"]["(multiple tab) sets state"] = function() left = 1001, right = 1002, }) - Helpers.expect.state(child, "tabs[1].wins.vsplits", 1) + Helpers.expect.state(child, "tabs[1].wins.vsplits[1000]", true) Helpers.expect.state_type(child, "tabs[1].wins.integrations", "table") @@ -294,7 +294,9 @@ T["enable"]["(multiple tab) sets state"] = function() left = 1004, right = 1005, }) - Helpers.expect.state(child, "tabs[2].wins.vsplits", 3) + Helpers.expect.state(child, "tabs[2].wins.vsplits[1003]", true) + Helpers.expect.state(child, "tabs[2].wins.vsplits[1004]", true) + Helpers.expect.state(child, "tabs[2].wins.vsplits[1005]", true) Helpers.expect.state_type(child, "tabs[2].wins.integrations", "table")