diff --git a/go.mod b/go.mod index e994a67b1..4e189992b 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/integrii/flaggy v1.4.0 github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d - github.com/jesseduffield/gocui v0.3.1-0.20240623092910-a42926c14fc9 + github.com/jesseduffield/gocui v0.3.1-0.20240623095254-05e1204c2454 github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e diff --git a/go.sum b/go.sum index 4a9156784..19c698189 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o= -github.com/jesseduffield/gocui v0.3.1-0.20240623092910-a42926c14fc9 h1:JJ0DrXgAUpGBGV5w8nzrQLMWTgcTvf745IKAk08qjcM= -github.com/jesseduffield/gocui v0.3.1-0.20240623092910-a42926c14fc9/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8= +github.com/jesseduffield/gocui v0.3.1-0.20240623095254-05e1204c2454 h1:rTPA5WiPM1SPUA3r2kSb3RiILC93am6irMvOLjO7JNA= +github.com/jesseduffield/gocui v0.3.1-0.20240623095254-05e1204c2454/go.mod h1:XtEbqCbn45keRXEu+OMZkjN5gw6AEob59afsgHjokZ8= github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0= github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo= github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY= diff --git a/pkg/gui/background.go b/pkg/gui/background.go index db267c0dc..061502a43 100644 --- a/pkg/gui/background.go +++ b/pkg/gui/background.go @@ -1,6 +1,8 @@ package gui import ( + "fmt" + "runtime" "strings" "time" @@ -46,6 +48,29 @@ func (self *BackgroundRoutineMgr) startBackgroundRoutines() { refreshInterval) } } + + if self.gui.Config.GetDebug() { + self.goEvery(time.Second*time.Duration(10), self.gui.stopChan, func() error { + formatBytes := func(b uint64) string { + const unit = 1000 + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := uint64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %cB", + float64(b)/float64(div), "kMGTPE"[exp]) + } + + m := runtime.MemStats{} + runtime.ReadMemStats(&m) + self.gui.c.Log.Infof("Heap memory in use: %s", formatBytes(m.HeapAlloc)) + return nil + }) + } } func (self *BackgroundRoutineMgr) startBackgroundFetch() { diff --git a/pkg/gui/context/base_context.go b/pkg/gui/context/base_context.go index acece1994..beaa61446 100644 --- a/pkg/gui/context/base_context.go +++ b/pkg/gui/context/base_context.go @@ -20,11 +20,12 @@ type BaseContext struct { onFocusFn onFocusFn onFocusLostFn onFocusLostFn - focusable bool - transient bool - hasControlledBounds bool - needsRerenderOnWidthChange bool - highlightOnFocus bool + focusable bool + transient bool + hasControlledBounds bool + needsRerenderOnWidthChange bool + needsRerenderOnHeightChange bool + highlightOnFocus bool *ParentContextMgr } @@ -37,15 +38,16 @@ type ( var _ types.IBaseContext = &BaseContext{} type NewBaseContextOpts struct { - Kind types.ContextKind - Key types.ContextKey - View *gocui.View - WindowName string - Focusable bool - Transient bool - HasUncontrolledBounds bool // negating for the sake of making false the default - HighlightOnFocus bool - NeedsRerenderOnWidthChange bool + Kind types.ContextKind + Key types.ContextKey + View *gocui.View + WindowName string + Focusable bool + Transient bool + HasUncontrolledBounds bool // negating for the sake of making false the default + HighlightOnFocus bool + NeedsRerenderOnWidthChange bool + NeedsRerenderOnHeightChange bool OnGetOptionsMap func() map[string]string } @@ -56,18 +58,19 @@ func NewBaseContext(opts NewBaseContextOpts) *BaseContext { hasControlledBounds := !opts.HasUncontrolledBounds return &BaseContext{ - kind: opts.Kind, - key: opts.Key, - view: opts.View, - windowName: opts.WindowName, - onGetOptionsMap: opts.OnGetOptionsMap, - focusable: opts.Focusable, - transient: opts.Transient, - hasControlledBounds: hasControlledBounds, - highlightOnFocus: opts.HighlightOnFocus, - needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange, - ParentContextMgr: &ParentContextMgr{}, - viewTrait: viewTrait, + kind: opts.Kind, + key: opts.Key, + view: opts.View, + windowName: opts.WindowName, + onGetOptionsMap: opts.OnGetOptionsMap, + focusable: opts.Focusable, + transient: opts.Transient, + hasControlledBounds: hasControlledBounds, + highlightOnFocus: opts.HighlightOnFocus, + needsRerenderOnWidthChange: opts.NeedsRerenderOnWidthChange, + needsRerenderOnHeightChange: opts.NeedsRerenderOnHeightChange, + ParentContextMgr: &ParentContextMgr{}, + viewTrait: viewTrait, } } @@ -197,6 +200,10 @@ func (self *BaseContext) NeedsRerenderOnWidthChange() bool { return self.needsRerenderOnWidthChange } +func (self *BaseContext) NeedsRerenderOnHeightChange() bool { + return self.needsRerenderOnHeightChange +} + func (self *BaseContext) Title() string { return "" } diff --git a/pkg/gui/context/list_context_trait.go b/pkg/gui/context/list_context_trait.go index c0e5ca04f..773f946a9 100644 --- a/pkg/gui/context/list_context_trait.go +++ b/pkg/gui/context/list_context_trait.go @@ -18,6 +18,9 @@ type ListContextTrait struct { // we should find out exactly which lines are now part of the path and refresh those. // We should also keep track of the previous path and refresh those lines too. refreshViewportOnChange bool + // If this is true, we only render the visible lines of the list. Useful for lists that can + // get very long, because it can save a lot of memory + renderOnlyVisibleLines bool } func (self *ListContextTrait) IsListContext() {} @@ -25,7 +28,8 @@ func (self *ListContextTrait) IsListContext() {} func (self *ListContextTrait) FocusLine() { // Doing this at the end of the layout function because we need the view to be // resized before we focus the line, otherwise if we're in accordion mode - // the view could be squashed and won't how to adjust the cursor/origin + // the view could be squashed and won't how to adjust the cursor/origin. + // Also, refreshing the viewport needs to happen after the view has been resized. self.c.AfterLayout(func() error { oldOrigin, _ := self.GetViewTrait().ViewPortYBounds() @@ -40,22 +44,18 @@ func (self *ListContextTrait) FocusLine() { self.GetViewTrait().CancelRangeSelect() } - // If FocusPoint() caused the view to scroll (because the selected line - // was out of view before), we need to rerender the view port again. - // This can happen when pressing , or . to scroll by pages, or < or > to - // jump to the top or bottom. - newOrigin, _ := self.GetViewTrait().ViewPortYBounds() - if self.refreshViewportOnChange && oldOrigin != newOrigin { + if self.refreshViewportOnChange { self.refreshViewport() + } else if self.renderOnlyVisibleLines { + newOrigin, _ := self.GetViewTrait().ViewPortYBounds() + if oldOrigin != newOrigin { + return self.HandleRender() + } } return nil }) self.setFooter() - - if self.refreshViewportOnChange { - self.refreshViewport() - } } func (self *ListContextTrait) refreshViewport() { @@ -93,8 +93,21 @@ func (self *ListContextTrait) HandleFocusLost(opts types.OnFocusLostOpts) error // OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view func (self *ListContextTrait) HandleRender() error { self.list.ClampSelection() - content := self.renderLines(-1, -1) - self.GetViewTrait().SetContent(content) + if self.renderOnlyVisibleLines { + // Rendering only the visible area can save a lot of cell memory for + // those views that support it. + totalLength := self.list.Len() + if self.getNonModelItems != nil { + totalLength += len(self.getNonModelItems()) + } + self.GetViewTrait().SetContentLineCount(totalLength) + startIdx, length := self.GetViewTrait().ViewPortYBounds() + content := self.renderLines(startIdx, startIdx+length) + self.GetViewTrait().SetViewPortContentAndClearEverythingElse(content) + } else { + content := self.renderLines(-1, -1) + self.GetViewTrait().SetContent(content) + } self.c.Render() self.setFooter() @@ -123,3 +136,7 @@ func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool { func (self *ListContextTrait) RangeSelectEnabled() bool { return true } + +func (self *ListContextTrait) RenderOnlyVisibleLines() bool { + return self.renderOnlyVisibleLines +} diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go index c02fa3fe6..ab42cfb70 100644 --- a/pkg/gui/context/local_commits_context.go +++ b/pkg/gui/context/local_commits_context.go @@ -72,12 +72,13 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext { SearchTrait: NewSearchTrait(c), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ - View: c.Views().Commits, - WindowName: "commits", - Key: LOCAL_COMMITS_CONTEXT_KEY, - Kind: types.SIDE_CONTEXT, - Focusable: true, - NeedsRerenderOnWidthChange: true, + View: c.Views().Commits, + WindowName: "commits", + Key: LOCAL_COMMITS_CONTEXT_KEY, + Kind: types.SIDE_CONTEXT, + Focusable: true, + NeedsRerenderOnWidthChange: true, + NeedsRerenderOnHeightChange: true, })), ListRenderer: ListRenderer{ list: viewModel, @@ -85,6 +86,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext { }, c: c, refreshViewportOnChange: true, + renderOnlyVisibleLines: true, }, } diff --git a/pkg/gui/context/remote_branches_context.go b/pkg/gui/context/remote_branches_context.go index 884d3debb..fff80e076 100644 --- a/pkg/gui/context/remote_branches_context.go +++ b/pkg/gui/context/remote_branches_context.go @@ -37,13 +37,14 @@ func NewRemoteBranchesContext( DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ - View: c.Views().RemoteBranches, - WindowName: "branches", - Key: REMOTE_BRANCHES_CONTEXT_KEY, - Kind: types.SIDE_CONTEXT, - Focusable: true, - Transient: true, - NeedsRerenderOnWidthChange: true, + View: c.Views().RemoteBranches, + WindowName: "branches", + Key: REMOTE_BRANCHES_CONTEXT_KEY, + Kind: types.SIDE_CONTEXT, + Focusable: true, + Transient: true, + NeedsRerenderOnWidthChange: true, + NeedsRerenderOnHeightChange: true, })), ListRenderer: ListRenderer{ list: viewModel, diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go index 842b364b3..ab0d2784a 100644 --- a/pkg/gui/context/sub_commits_context.go +++ b/pkg/gui/context/sub_commits_context.go @@ -115,13 +115,14 @@ func NewSubCommitsContext( DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ - View: c.Views().SubCommits, - WindowName: "branches", - Key: SUB_COMMITS_CONTEXT_KEY, - Kind: types.SIDE_CONTEXT, - Focusable: true, - Transient: true, - NeedsRerenderOnWidthChange: true, + View: c.Views().SubCommits, + WindowName: "branches", + Key: SUB_COMMITS_CONTEXT_KEY, + Kind: types.SIDE_CONTEXT, + Focusable: true, + Transient: true, + NeedsRerenderOnWidthChange: true, + NeedsRerenderOnHeightChange: true, })), ListRenderer: ListRenderer{ list: viewModel, @@ -130,6 +131,7 @@ func NewSubCommitsContext( }, c: c, refreshViewportOnChange: true, + renderOnlyVisibleLines: true, }, } diff --git a/pkg/gui/context/view_trait.go b/pkg/gui/context/view_trait.go index 1179a8b14..191419897 100644 --- a/pkg/gui/context/view_trait.go +++ b/pkg/gui/context/view_trait.go @@ -34,6 +34,15 @@ func (self *ViewTrait) SetViewPortContent(content string) { self.view.OverwriteLines(y, content) } +func (self *ViewTrait) SetViewPortContentAndClearEverythingElse(content string) { + _, y := self.view.Origin() + self.view.OverwriteLinesAndClearEverythingElse(y, content) +} + +func (self *ViewTrait) SetContentLineCount(lineCount int) { + self.view.SetContentLineCount(lineCount) +} + func (self *ViewTrait) SetContent(content string) { self.view.SetContent(content) } diff --git a/pkg/gui/controllers/list_controller.go b/pkg/gui/controllers/list_controller.go index dc876b3fc..711d32f79 100644 --- a/pkg/gui/controllers/list_controller.go +++ b/pkg/gui/controllers/list_controller.go @@ -53,6 +53,9 @@ func (self *ListController) HandleScrollRight() error { func (self *ListController) HandleScrollUp() error { scrollHeight := self.c.UserConfig.Gui.ScrollHeight self.context.GetViewTrait().ScrollUp(scrollHeight) + if self.context.RenderOnlyVisibleLines() { + return self.context.HandleRender() + } return nil } @@ -60,6 +63,9 @@ func (self *ListController) HandleScrollUp() error { func (self *ListController) HandleScrollDown() error { scrollHeight := self.c.UserConfig.Gui.ScrollHeight self.context.GetViewTrait().ScrollDown(scrollHeight) + if self.context.RenderOnlyVisibleLines() { + return self.context.HandleRender() + } return nil } diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go index 01c397628..4e2b49477 100644 --- a/pkg/gui/layout.go +++ b/pkg/gui/layout.go @@ -72,14 +72,26 @@ func (gui *Gui) layout(g *gocui.Gui) error { frameOffset = 0 } + mustRerender := false if context.NeedsRerenderOnWidthChange() { // view.Width() returns the width -1 for some reason oldWidth := view.Width() + 1 newWidth := dimensionsObj.X1 - dimensionsObj.X0 + 2*frameOffset if oldWidth != newWidth { - contextsToRerender = append(contextsToRerender, context) + mustRerender = true } } + if context.NeedsRerenderOnHeightChange() { + // view.Height() returns the height -1 for some reason + oldHeight := view.Height() + 1 + newHeight := dimensionsObj.Y1 - dimensionsObj.Y0 + 2*frameOffset + if oldHeight != newHeight { + mustRerender = true + } + } + if mustRerender { + contextsToRerender = append(contextsToRerender, context) + } _, err = g.SetView( viewName, diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index b0e312c97..691d5694d 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -63,6 +63,9 @@ type IBaseContext interface { // true if the view needs to be rerendered when its width changes NeedsRerenderOnWidthChange() bool + // true if the view needs to be rerendered when its height changes + NeedsRerenderOnHeightChange() bool + // returns the desired title for the view upon activation. If there is no desired title (returns empty string), then // no title will be set Title() string @@ -150,6 +153,7 @@ type IListContext interface { FocusLine() IsListContext() // used for type switch RangeSelectEnabled() bool + RenderOnlyVisibleLines() bool } type IPatchExplorerContext interface { @@ -172,6 +176,8 @@ type IViewTrait interface { SetRangeSelectStart(yIdx int) CancelRangeSelect() SetViewPortContent(content string) + SetViewPortContentAndClearEverythingElse(content string) + SetContentLineCount(lineCount int) SetContent(content string) SetFooter(value string) SetOriginX(value int) diff --git a/pkg/integration/components/view_driver.go b/pkg/integration/components/view_driver.go index b6f917603..3abd63a9e 100644 --- a/pkg/integration/components/view_driver.go +++ b/pkg/integration/components/view_driver.go @@ -431,6 +431,11 @@ func (self *ViewDriver) SelectPreviousItem() *ViewDriver { return self.PressFast(self.t.keys.Universal.PrevItem) } +// i.e. pressing '<' +func (self *ViewDriver) GotoTop() *ViewDriver { + return self.PressFast(self.t.keys.Universal.GotoTop) +} + // i.e. pressing space func (self *ViewDriver) PressPrimaryAction() *ViewDriver { return self.Press(self.t.keys.Universal.Select) @@ -457,21 +462,15 @@ func (self *ViewDriver) PressEscape() *ViewDriver { // - the user is not in a list item // - no list item is found containing the given text // - multiple list items are found containing the given text in the initial page of items -// -// NOTE: this currently assumes that BufferLines returns all the lines that can be accessed. -// If this changes in future, we'll need to update this code to first attempt to find the item -// in the current page and failing that, jump to the top of the view and iterate through all of it, -// looking for the item. func (self *ViewDriver) NavigateToLine(matcher *TextMatcher) *ViewDriver { self.IsFocused() view := self.getView() lines := view.BufferLines() - var matchIndex int + matchIndex := -1 self.t.assertWithRetries(func() (bool, string) { - matchIndex = -1 var matches []string // first we look for a duplicate on the current screen. We won't bother looking beyond that though. for i, line := range lines { @@ -483,13 +482,19 @@ func (self *ViewDriver) NavigateToLine(matcher *TextMatcher) *ViewDriver { } if len(matches) > 1 { return false, fmt.Sprintf("Found %d matches for `%s`, expected only a single match. Matching lines:\n%s", len(matches), matcher.name(), strings.Join(matches, "\n")) - } else if len(matches) == 0 { - return false, fmt.Sprintf("Could not find item matching: %s. Lines:\n%s", matcher.name(), strings.Join(lines, "\n")) - } else { - return true, "" } + return true, "" }) + // If no match was found, it could be that this is a view that renders only + // the visible lines. In that case, we jump to the top and then press + // down-arrow until we found the match. We simply return the first match we + // find, so we have no way to assert that there are no duplicates. + if matchIndex == -1 { + self.GotoTop() + matchIndex = len(lines) + } + selectedLineIdx := self.getSelectedLineIdx() if selectedLineIdx == matchIndex { return self.SelectedLine(matcher) @@ -514,12 +519,14 @@ func (self *ViewDriver) NavigateToLine(matcher *TextMatcher) *ViewDriver { for i := 0; i < maxNumKeyPresses; i++ { keyPress() idx := self.getSelectedLineIdx() - if ok, _ := matcher.test(lines[idx]); ok { + // It is important to use view.BufferLines() here and not lines, because it + // could change with every keypress. + if ok, _ := matcher.test(view.BufferLines()[idx]); ok { return self } } - self.t.fail(fmt.Sprintf("Could not navigate to item matching: %s. Lines:\n%s", matcher.name(), strings.Join(lines, "\n"))) + self.t.fail(fmt.Sprintf("Could not navigate to item matching: %s. Lines:\n%s", matcher.name(), strings.Join(view.BufferLines(), "\n"))) return self } diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 68b0f49c6..a32519b80 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -771,13 +771,14 @@ func (v *View) writeRunes(p []rune) { } v.wx = 0 default: - moveCursor, cells := v.parseInput(r, v.wx, v.wy) + truncateLine, cells := v.parseInput(r, v.wx, v.wy) if cells == nil { continue } v.writeCells(v.wx, v.wy, cells) - if moveCursor { - v.wx += len(cells) + v.wx += len(cells) + if truncateLine { + v.lines[v.wy] = v.lines[v.wy][:v.wx] } } } @@ -800,7 +801,7 @@ func (v *View) writeString(s string) { // contains the processed data. func (v *View) parseInput(ch rune, x int, _ int) (bool, []cell) { cells := []cell{} - moveCursor := true + truncateLine := false isEscape, err := v.ei.parseOne(ch) if err != nil { @@ -816,18 +817,13 @@ func (v *View) parseInput(ch rune, x int, _ int) (bool, []cell) { } else { repeatCount := 1 if _, ok := v.ei.instruction.(eraseInLineFromCursor); ok { - // fill rest of line + // truncate line v.ei.instructionRead() - cx := 0 - for _, cell := range v.lines[v.wy] { - cx += runewidth.RuneWidth(cell.chr) - } - repeatCount = v.InnerWidth() - cx - ch = ' ' - moveCursor = false + repeatCount = 0 + truncateLine = true } else if isEscape { // do not output anything - return moveCursor, nil + return truncateLine, nil } else if ch == '\t' { // fill tab-sized space const tabStop = 4 @@ -844,7 +840,7 @@ func (v *View) parseInput(ch rune, x int, _ int) (bool, []cell) { } } - return moveCursor, cells + return truncateLine, cells } // Read reads data into p from the current reading position set by SetReadPos. @@ -1590,19 +1586,51 @@ func (v *View) ClearTextArea() { _ = v.SetCursor(0, 0) } -// only call this function if you don't care where v.wx and v.wy end up -func (v *View) OverwriteLines(y int, content string) { - v.writeMutex.Lock() - defer v.writeMutex.Unlock() - +func (v *View) overwriteLines(y int, content string) { // break by newline, then for each line, write it, then add that erase command v.wx = 0 v.wy = y lines := strings.Replace(content, "\n", "\x1b[K\n", -1) + // If the last line doesn't end with a linefeed, add the erase command at + // the end too + if !strings.HasSuffix(lines, "\n") { + lines += "\x1b[K" + } v.writeString(lines) } +// only call this function if you don't care where v.wx and v.wy end up +func (v *View) OverwriteLines(y int, content string) { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + v.overwriteLines(y, content) +} + +// only call this function if you don't care where v.wx and v.wy end up +func (v *View) OverwriteLinesAndClearEverythingElse(y int, content string) { + v.writeMutex.Lock() + defer v.writeMutex.Unlock() + + v.overwriteLines(y, content) + + for i := 0; i < y; i += 1 { + v.lines[i] = nil + } + + for i := v.wy + 1; i < len(v.lines); i += 1 { + v.lines[i] = nil + } +} + +func (v *View) SetContentLineCount(lineCount int) { + if lineCount > 0 { + v.makeWriteable(0, lineCount-1) + } + v.lines = v.lines[:lineCount] +} + func (v *View) ScrollUp(amount int) { if amount > v.oy { amount = v.oy diff --git a/vendor/modules.txt b/vendor/modules.txt index 47793de1e..62d47eea4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -172,7 +172,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem github.com/jesseduffield/go-git/v5/utils/merkletrie/index github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame github.com/jesseduffield/go-git/v5/utils/merkletrie/noder -# github.com/jesseduffield/gocui v0.3.1-0.20240623092910-a42926c14fc9 +# github.com/jesseduffield/gocui v0.3.1-0.20240623095254-05e1204c2454 ## explicit; go 1.12 github.com/jesseduffield/gocui # github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10