From bcfb4e11c0b7932dde8a834260551952071b12d3 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 22 Feb 2026 14:42:49 +0100 Subject: [PATCH 1/2] Fix off-by-one error when calculating popup panel dimensions The symptom of the bug was that confirmation panels with wrapped text were one character too wide, which could sometimes result in them being one line too heigh. One concrete example is the "Discard file changes" confirmation that appears when pressing `d` in the commit files panel. I got very confused when investigating this bug, and the reason is that getPopupPanelDimensionsAux took two parameters panelWidth and panelHeight, but the second was actually contentHeight. Rename it to contentHeight, and change the first one to also be the content width rather than the panel width. This is easier for clients, and less confusing. --- .../helpers/confirmation_helper.go | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/pkg/gui/controllers/helpers/confirmation_helper.go b/pkg/gui/controllers/helpers/confirmation_helper.go index 162d8b48e..da87063ae 100644 --- a/pkg/gui/controllers/helpers/confirmation_helper.go +++ b/pkg/gui/controllers/helpers/confirmation_helper.go @@ -102,11 +102,13 @@ func getMessageHeight(wrap bool, editable bool, message string, width int, tabWi return len(wrappedLines) } -func (self *ConfirmationHelper) getPopupPanelDimensionsForContentHeight(panelWidth, contentHeight int, parentPopupContext types.Context) (int, int, int, int) { - return self.getPopupPanelDimensionsAux(panelWidth, contentHeight, parentPopupContext) +func (self *ConfirmationHelper) getPopupPanelDimensionsForContentHeight(contentWidth, contentHeight int, parentPopupContext types.Context) (int, int, int, int) { + return self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext) } -func (self *ConfirmationHelper) getPopupPanelDimensionsAux(panelWidth int, panelHeight int, parentPopupContext types.Context) (int, int, int, int) { +func (self *ConfirmationHelper) getPopupPanelDimensionsAux(contentWidth int, contentHeight int, parentPopupContext types.Context) (int, int, int, int) { + panelWidth := contentWidth + 2 // plus 2 for the frame + panelHeight := contentHeight + 2 width, height := self.c.GocuiGui().Size() if panelHeight > height*3/4 { panelHeight = height * 3 / 4 @@ -117,12 +119,15 @@ func (self *ConfirmationHelper) getPopupPanelDimensionsAux(panelWidth int, panel x0, y0, _, _ := parentPopupContext.GetView().Dimensions() x0 += 2 y0 += 1 - return x0, y0, x0 + panelWidth, y0 + panelHeight + 1 + return x0, y0, x0 + panelWidth - 1, y0 + panelHeight - 1 } return width/2 - panelWidth/2, - height/2 - panelHeight/2 - panelHeight%2 - 1, - width/2 + panelWidth/2, - height/2 + panelHeight/2 + height/2 - panelHeight/2 - panelHeight%2, + // Currently, X1/Y1 of a gocui view is one less than you would expect based on its + // width/height, so we need to subtract 1 here. See + // https://github.com/jesseduffield/lazygit/commit/f6f2a52dee8bba3ebd7e3b34b4b7c7d3e3795f3e + width/2 + panelWidth/2 - 1, + height/2 + panelHeight/2 - 1 } func (self *ConfirmationHelper) getPopupPanelWidth(maxWidth int) int { @@ -331,7 +336,7 @@ func (self *ConfirmationHelper) resizeMenu(parentPopupContext types.Context) { panelWidth := self.getPopupPanelWidth(90) contentWidth := panelWidth - 2 // minus 2 for the frame promptLinesCount := self.layoutMenuPrompt(contentWidth) - x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(panelWidth, itemCount+offset+promptLinesCount, parentPopupContext) + x0, y0, x1, y1 := self.getPopupPanelDimensionsForContentHeight(contentWidth, itemCount+offset+promptLinesCount, parentPopupContext) menuBottom := y1 - offset _, _ = self.c.GocuiGui().SetView(self.c.Views().Menu.Name(), x0, y0, x1, menuBottom, 0) @@ -379,8 +384,8 @@ func (self *ConfirmationHelper) resizeConfirmationPanel(parentPopupContext types contentWidth := panelWidth - 2 // minus 2 for the frame confirmationView := self.c.Views().Confirmation prompt := confirmationView.Buffer() - panelHeight := getMessageHeight(true, false, prompt, contentWidth, confirmationView.TabWidth) - x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext) + contentHeight := getMessageHeight(true, false, prompt, contentWidth, confirmationView.TabWidth) + x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext) _, _ = self.c.GocuiGui().SetView(confirmationView.Name(), x0, y0, x1, y1, 0) } @@ -393,8 +398,8 @@ func (self *ConfirmationHelper) resizePromptPanel(parentPopupContext types.Conte contentWidth := panelWidth - 2 // minus 2 for the frame promptView := self.c.Views().Prompt prompt := promptView.TextArea.GetContent() - panelHeight := getMessageHeight(false, true, prompt, contentWidth, promptView.TabWidth) + suggestionsViewHeight - x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext) + contentHeight := getMessageHeight(false, true, prompt, contentWidth, promptView.TabWidth) + suggestionsViewHeight + x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext) promptViewBottom := y1 - suggestionsViewHeight _, _ = self.c.GocuiGui().SetView(promptView.Name(), x0, y0, x1, promptViewBottom, 0) @@ -410,14 +415,15 @@ func (self *ConfirmationHelper) ResizeCommitMessagePanels(parentPopupContext typ maxWidth = self.c.UserConfig().Git.Commit.AutoWrapWidth + 25 } panelWidth := self.getPopupPanelWidth(maxWidth) + contentWidth := panelWidth - 2 // minus 2 for the frame content := self.c.Views().CommitDescription.TextArea.GetContent() summaryViewHeight := 3 - panelHeight := getMessageHeight(false, true, content, panelWidth, self.c.Views().CommitDescription.TabWidth) + contentHeight := getMessageHeight(false, true, content, contentWidth, self.c.Views().CommitDescription.TabWidth) minHeight := 7 - if panelHeight < minHeight { - panelHeight = minHeight + if contentHeight < minHeight { + contentHeight = minHeight } - x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext) + x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(contentWidth, contentHeight, parentPopupContext) _, _ = self.c.GocuiGui().SetView(self.c.Views().CommitMessage.Name(), x0, y0, x1, y0+summaryViewHeight-1, 0) _, _ = self.c.GocuiGui().SetView(self.c.Views().CommitDescription.Name(), x0, y0+summaryViewHeight, x1, y1+summaryViewHeight, 0) From da065b5b630a81571ff5c5f917259d900a24b2f6 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 22 Feb 2026 17:41:28 +0100 Subject: [PATCH 2/2] Add comments I got confused when I investigated the bug that was fixed in the previous commit, so hopefully these will be helpful for the next person reading this code. --- pkg/gui/controllers/helpers/confirmation_helper.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/gui/controllers/helpers/confirmation_helper.go b/pkg/gui/controllers/helpers/confirmation_helper.go index da87063ae..3663cd4ea 100644 --- a/pkg/gui/controllers/helpers/confirmation_helper.go +++ b/pkg/gui/controllers/helpers/confirmation_helper.go @@ -130,6 +130,8 @@ func (self *ConfirmationHelper) getPopupPanelDimensionsAux(contentWidth int, con height/2 + panelHeight/2 - 1 } +// Returns the outer width of the view, including its frame. To decide how to wrap text, subtract 2. +// Also, note that X1-X0 of the view is one less than this. func (self *ConfirmationHelper) getPopupPanelWidth(maxWidth int) int { width, _ := self.c.GocuiGui().Size() // we want a minimum width up to a point, then we do it based on ratio, but only up to the given max width @@ -418,6 +420,8 @@ func (self *ConfirmationHelper) ResizeCommitMessagePanels(parentPopupContext typ contentWidth := panelWidth - 2 // minus 2 for the frame content := self.c.Views().CommitDescription.TextArea.GetContent() summaryViewHeight := 3 + // The width we pass to getMessageHeight is irrelevant because we don't wrap the text to the + // view width. If we did, we'd have to subtract 2 for the view frame. contentHeight := getMessageHeight(false, true, content, contentWidth, self.c.Views().CommitDescription.TabWidth) minHeight := 7 if contentHeight < minHeight {