diff --git a/go.mod b/go.mod index f53f2645e..73124a6d1 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.20201006095850-341962be15a4 - github.com/jesseduffield/gocui v0.3.1-0.20220415005542-2eb424ce3d0a + github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67 github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e github.com/jesseduffield/yaml v2.1.0+incompatible github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 diff --git a/go.sum b/go.sum index 7dbd3f1b8..0a5b19b6c 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,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.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg= github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o= -github.com/jesseduffield/gocui v0.3.1-0.20220415005542-2eb424ce3d0a h1:Fd8B7eZJfwK0cFzqz2gEFLEDiY0iwJTm6oBbPRHONxA= -github.com/jesseduffield/gocui v0.3.1-0.20220415005542-2eb424ce3d0a/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU= +github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67 h1:6NIOoR4LMuNEkP6e9P6GVZkzgYZ7rqpfM+LieKqubnA= +github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU= github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U= github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I= github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE= diff --git a/pkg/gui/context/view_trait.go b/pkg/gui/context/view_trait.go index 4c0cd9e05..8f2a2e99e 100644 --- a/pkg/gui/context/view_trait.go +++ b/pkg/gui/context/view_trait.go @@ -3,7 +3,6 @@ package context import ( "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/jesseduffield/lazygit/pkg/utils" ) const HORIZONTAL_SCROLL_FACTOR = 3 @@ -43,20 +42,31 @@ func (self *ViewTrait) SetOriginX(value int) { _ = self.view.SetOriginX(value) } -// tells us the bounds of line indexes shown in the view currently +// tells us the start of line indexes shown in the view currently as well as the capacity of lines shown in the viewport. func (self *ViewTrait) ViewPortYBounds() (int, int) { - _, min := self.view.Origin() - max := self.view.InnerHeight() + 1 - return min, max + _, start := self.view.Origin() + length := self.view.InnerHeight() + 1 + return start, length } func (self *ViewTrait) ScrollLeft() { - newOriginX := utils.Max(self.view.OriginX()-self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0) - _ = self.view.SetOriginX(newOriginX) + self.view.ScrollLeft(self.horizontalScrollAmount()) } func (self *ViewTrait) ScrollRight() { - _ = self.view.SetOriginX(self.view.OriginX() + self.view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR) + self.view.ScrollRight(self.horizontalScrollAmount()) +} + +func (self *ViewTrait) horizontalScrollAmount() int { + return self.view.InnerWidth() / HORIZONTAL_SCROLL_FACTOR +} + +func (self *ViewTrait) ScrollUp() { + self.view.ScrollUp(1) +} + +func (self *ViewTrait) ScrollDown() { + self.view.ScrollDown(1) } // this returns the amount we'll scroll if we want to scroll by a page. diff --git a/pkg/gui/context/viewport_list_context_trait.go b/pkg/gui/context/viewport_list_context_trait.go index ab9b04fb8..b89dea832 100644 --- a/pkg/gui/context/viewport_list_context_trait.go +++ b/pkg/gui/context/viewport_list_context_trait.go @@ -15,8 +15,8 @@ type ViewportListContextTrait struct { func (self *ViewportListContextTrait) FocusLine() { self.ListContextTrait.FocusLine() - min, max := self.GetViewTrait().ViewPortYBounds() - displayStrings := self.ListContextTrait.getDisplayStrings(min, max) + startIdx, length := self.GetViewTrait().ViewPortYBounds() + displayStrings := self.ListContextTrait.getDisplayStrings(startIdx, length) content := utils.RenderDisplayStrings(displayStrings) self.GetViewTrait().SetViewPortContent(content) } diff --git a/pkg/gui/controllers/list_controller.go b/pkg/gui/controllers/list_controller.go index 0f64228bb..7da05710e 100644 --- a/pkg/gui/controllers/list_controller.go +++ b/pkg/gui/controllers/list_controller.go @@ -43,14 +43,42 @@ func (self *ListController) HandleNextLine() error { } func (self *ListController) HandleScrollLeft() error { - return self.scroll(self.context.GetViewTrait().ScrollLeft) + return self.scrollHorizontal(self.context.GetViewTrait().ScrollLeft) } func (self *ListController) HandleScrollRight() error { - return self.scroll(self.context.GetViewTrait().ScrollRight) + return self.scrollHorizontal(self.context.GetViewTrait().ScrollRight) } -func (self *ListController) scroll(scrollFunc func()) error { +func (self *ListController) HandleScrollUp() error { + self.context.GetViewTrait().ScrollUp() + + // we only need to do a line change if our line has been pushed out of the viewport, because + // at the moment much logic depends on the selected line always being visible + if !self.isSelectedLineInViewPort() { + return self.handleLineChange(-1) + } + + return nil +} + +func (self *ListController) HandleScrollDown() error { + self.context.GetViewTrait().ScrollDown() + + if !self.isSelectedLineInViewPort() { + return self.handleLineChange(1) + } + + return nil +} + +func (self *ListController) isSelectedLineInViewPort() bool { + selectedLineIdx := self.context.GetList().GetSelectedLineIdx() + startIdx, length := self.context.GetViewTrait().ViewPortYBounds() + return selectedLineIdx >= startIdx && selectedLineIdx < startIdx+length +} + +func (self *ListController) scrollHorizontal(scrollFunc func()) error { scrollFunc() return self.context.HandleFocus() @@ -157,7 +185,7 @@ func (self *ListController) GetMouseKeybindings(opts types.KeybindingsOpts) []*g ViewName: self.context.GetViewName(), ToContext: string(self.context.GetKey()), Key: gocui.MouseWheelUp, - Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandlePrevLine() }, + Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollUp() }, }, { ViewName: self.context.GetViewName(), @@ -169,7 +197,7 @@ func (self *ListController) GetMouseKeybindings(opts types.KeybindingsOpts) []*g ViewName: self.context.GetViewName(), ToContext: string(self.context.GetKey()), Key: gocui.MouseWheelDown, - Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleNextLine() }, + Handler: func(gocui.ViewMouseBindingOpts) error { return self.HandleScrollDown() }, }, } } diff --git a/pkg/gui/extras_panel.go b/pkg/gui/extras_panel.go index 462a5118e..6033f8516 100644 --- a/pkg/gui/extras_panel.go +++ b/pkg/gui/extras_panel.go @@ -46,15 +46,15 @@ func (gui *Gui) handleFocusCommandLog() error { func (gui *Gui) scrollUpExtra() error { gui.Views.Extras.Autoscroll = false - return gui.scrollUpView(gui.Views.Extras) + gui.scrollUpView(gui.Views.Extras) + + return nil } func (gui *Gui) scrollDownExtra() error { gui.Views.Extras.Autoscroll = false - if err := gui.scrollDownView(gui.Views.Extras); err != nil { - return err - } + gui.scrollDownView(gui.Views.Extras) return nil } diff --git a/pkg/gui/global_handlers.go b/pkg/gui/global_handlers.go index e76c9f1a6..97cf4650d 100644 --- a/pkg/gui/global_handlers.go +++ b/pkg/gui/global_handlers.go @@ -2,7 +2,6 @@ package gui import ( "fmt" - "math" "strings" "github.com/jesseduffield/gocui" @@ -63,56 +62,17 @@ func (gui *Gui) prevScreenMode() error { return gui.rerenderViewsWithScreenModeDependentContent() } -func (gui *Gui) scrollUpView(view *gocui.View) error { - ox, oy := view.Origin() - newOy := int(math.Max(0, float64(oy-gui.c.UserConfig.Gui.ScrollHeight))) - return view.SetOrigin(ox, newOy) +func (gui *Gui) scrollUpView(view *gocui.View) { + view.ScrollUp(gui.c.UserConfig.Gui.ScrollHeight) } -func (gui *Gui) scrollDownView(view *gocui.View) error { - ox, oy := view.Origin() - scrollHeight := gui.linesToScrollDown(view) - if scrollHeight > 0 { - if err := view.SetOrigin(ox, oy+scrollHeight); err != nil { - return err - } - } +func (gui *Gui) scrollDownView(view *gocui.View) { + scrollHeight := gui.c.UserConfig.Gui.ScrollHeight + view.ScrollDown(scrollHeight) if manager, ok := gui.viewBufferManagerMap[view.Name()]; ok { manager.ReadLines(scrollHeight) } - return nil -} - -func (gui *Gui) linesToScrollDown(view *gocui.View) int { - _, oy := view.Origin() - y := oy - canScrollPastBottom := gui.c.UserConfig.Gui.ScrollPastBottom - if !canScrollPastBottom { - _, sy := view.Size() - y += sy - } - scrollHeight := gui.c.UserConfig.Gui.ScrollHeight - scrollableLines := view.ViewLinesHeight() - y - if scrollableLines < 0 { - return 0 - } - - // margin is about how many lines must still appear if you scroll - // all the way down. In practice every file ends in a newline so it will really - // just show a single line - margin := 1 - if canScrollPastBottom { - margin = 2 - } - if scrollableLines-margin < scrollHeight { - scrollHeight = scrollableLines - margin - } - if oy+scrollHeight < 0 { - return 0 - } else { - return scrollHeight - } } func (gui *Gui) scrollUpMain() error { @@ -120,7 +80,9 @@ func (gui *Gui) scrollUpMain() error { gui.State.Panels.Merging.UserVerticalScrolling = true } - return gui.scrollUpView(gui.Views.Main) + gui.scrollUpView(gui.Views.Main) + + return nil } func (gui *Gui) scrollDownMain() error { @@ -128,7 +90,9 @@ func (gui *Gui) scrollDownMain() error { gui.State.Panels.Merging.UserVerticalScrolling = true } - return gui.scrollDownView(gui.Views.Main) + gui.scrollDownView(gui.Views.Main) + + return nil } func (gui *Gui) scrollLeftMain() error { @@ -153,11 +117,15 @@ func (gui *Gui) scrollRight(view *gocui.View) { } func (gui *Gui) scrollUpSecondary() error { - return gui.scrollUpView(gui.Views.Secondary) + gui.scrollUpView(gui.Views.Secondary) + + return nil } func (gui *Gui) scrollDownSecondary() error { - return gui.scrollDownView(gui.Views.Secondary) + gui.scrollDownView(gui.Views.Secondary) + + return nil } func (gui *Gui) scrollUpConfirmationPanel() error { @@ -165,7 +133,9 @@ func (gui *Gui) scrollUpConfirmationPanel() error { return nil } - return gui.scrollUpView(gui.Views.Confirmation) + gui.scrollUpView(gui.Views.Confirmation) + + return nil } func (gui *Gui) scrollDownConfirmationPanel() error { @@ -173,7 +143,9 @@ func (gui *Gui) scrollDownConfirmationPanel() error { return nil } - return gui.scrollDownView(gui.Views.Confirmation) + gui.scrollDownView(gui.Views.Confirmation) + + return nil } func (gui *Gui) handleRefresh() error { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 17ca26f41..23b8ae8ca 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -661,11 +661,13 @@ func (gui *Gui) createAllViews() error { gui.Views.Secondary.Wrap = true gui.Views.Secondary.FgColor = theme.GocuiDefaultTextColor gui.Views.Secondary.IgnoreCarriageReturns = true + gui.Views.Secondary.CanScrollPastBottom = gui.c.UserConfig.Gui.ScrollPastBottom gui.Views.Main.Title = gui.c.Tr.DiffTitle gui.Views.Main.Wrap = true gui.Views.Main.FgColor = theme.GocuiDefaultTextColor gui.Views.Main.IgnoreCarriageReturns = true + gui.Views.Main.CanScrollPastBottom = gui.c.UserConfig.Gui.ScrollPastBottom gui.Views.Limit.Title = gui.c.Tr.NotEnoughSpace gui.Views.Limit.Wrap = true diff --git a/pkg/gui/tasks_adapter.go b/pkg/gui/tasks_adapter.go index d619c58ba..745cc3760 100644 --- a/pkg/gui/tasks_adapter.go +++ b/pkg/gui/tasks_adapter.go @@ -98,21 +98,15 @@ func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager { }, func() { // Need to check if the content of the view is well past the origin. - // It would be better to use .ViewLinesHeight here (given it considers - // wrapping) but when this function is called they haven't been written to yet. - linesHeight := view.LinesHeight() - _, height := view.Size() + linesHeight := view.ViewLinesHeight() _, originY := view.Origin() if linesHeight < originY { - newOriginY := linesHeight - height - if newOriginY < 0 { - newOriginY = 0 - } + newOriginY := linesHeight + err := view.SetOrigin(0, newOriginY) if err != nil { panic(err) } - } view.FlushStaleCells() diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index 6f026fe73..253bb62ef 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -85,6 +85,8 @@ type IViewTrait interface { ViewPortYBounds() (int, int) ScrollLeft() ScrollRight() + ScrollUp() + ScrollDown() PageDelta() int SelectedLineIdx() int } diff --git a/vendor/github.com/jesseduffield/gocui/gui.go b/vendor/github.com/jesseduffield/gocui/gui.go index ba8d93ce2..58e396492 100644 --- a/vendor/github.com/jesseduffield/gocui/gui.go +++ b/vendor/github.com/jesseduffield/gocui/gui.go @@ -743,6 +743,8 @@ func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error { } } } + + showScrollbar, realScrollbarStart, realScrollbarEnd := calcRealScrollbarStartEnd(v) for y := v.y0 + 1; y < v.y1 && y < g.maxY; y++ { if y < 0 { continue @@ -753,7 +755,9 @@ func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error { } } if v.x1 > -1 && v.x1 < g.maxX { - if err := g.SetRune(v.x1, y, runeV, fgColor, bgColor); err != nil { + runeToPrint := calcScrollbarRune(showScrollbar, realScrollbarStart, realScrollbarEnd, v.y0+1, v.y1-1, y, runeV) + + if err := g.SetRune(v.x1, y, runeToPrint, fgColor, bgColor); err != nil { return err } } @@ -761,6 +765,44 @@ func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error { return nil } +func calcScrollbarRune(showScrollbar bool, scrollbarStart int, scrollbarEnd int, rangeStart int, rangeEnd int, position int, runeV rune) rune { + if !showScrollbar { + return runeV + } else if position == rangeStart { + return '▲' + } else if position == rangeEnd { + return '▼' + } else if position > scrollbarStart && position < scrollbarEnd { + return '█' + } else if position > rangeStart && position < rangeEnd { + // keeping this as a separate branch in case we later want to render something different here. + return runeV + } else { + return runeV + } +} + +func calcRealScrollbarStartEnd(v *View) (bool, int, int) { + height := v.InnerHeight() + 1 + fullHeight := v.ViewLinesHeight() - v.scrollMargin() + + if v.CanScrollPastBottom { + fullHeight += height + } + + if height < 2 || height >= fullHeight { + return false, 0, 0 + } + + originY := v.OriginY() + scrollbarStart, scrollbarHeight := calcScrollbar(fullHeight, height, originY, height-1) + top := v.y0 + 1 + realScrollbarStart := top + scrollbarStart + realScrollbarEnd := realScrollbarStart + scrollbarHeight + + return true, realScrollbarStart, realScrollbarEnd +} + func cornerRune(index byte) rune { return []rune{' ', '│', '│', '│', '─', '┘', '┐', '┤', '─', '└', '┌', '├', '├', '┴', '┬', '┼'}[index] } @@ -1014,6 +1056,40 @@ func (g *Gui) draw(v *View) error { if !v.Visible || v.y1 < v.y0 { return nil } + + if g.Cursor { + if curview := g.currentView; curview != nil { + vMaxX, vMaxY := curview.Size() + if curview.cx < 0 { + curview.cx = 0 + } else if curview.cx >= vMaxX { + curview.cx = vMaxX - 1 + } + if curview.cy < 0 { + curview.cy = 0 + } else if curview.cy >= vMaxY { + curview.cy = vMaxY - 1 + } + + gMaxX, gMaxY := g.Size() + cx, cy := curview.x0+curview.cx+1, curview.y0+curview.cy+1 + // This test probably doesn't need to be here. + // tcell is hiding cursor by setting coordinates outside of screen. + // Keeping it here for now, as I'm not 100% sure :) + if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY { + Screen.ShowCursor(cx, cy) + } else { + Screen.HideCursor() + } + } + } else { + Screen.HideCursor() + } + + if err := v.draw(); err != nil { + return err + } + if v.Frame { var fgColor, bgColor, frameColor Attribute if g.Highlight && v == g.currentView { @@ -1057,38 +1133,6 @@ func (g *Gui) draw(v *View) error { } } - if g.Cursor { - if curview := g.currentView; curview != nil { - vMaxX, vMaxY := curview.Size() - if curview.cx < 0 { - curview.cx = 0 - } else if curview.cx >= vMaxX { - curview.cx = vMaxX - 1 - } - if curview.cy < 0 { - curview.cy = 0 - } else if curview.cy >= vMaxY { - curview.cy = vMaxY - 1 - } - - gMaxX, gMaxY := g.Size() - cx, cy := curview.x0+curview.cx+1, curview.y0+curview.cy+1 - // This test probably doesn't need to be here. - // tcell is hiding cursor by setting coordinates outside of screen. - // Keeping it here for now, as I'm not 100% sure :) - if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY { - Screen.ShowCursor(cx, cy) - } else { - Screen.HideCursor() - } - } - } else { - Screen.HideCursor() - } - - if err := v.draw(); err != nil { - return err - } return nil } diff --git a/vendor/github.com/jesseduffield/gocui/scrollbar.go b/vendor/github.com/jesseduffield/gocui/scrollbar.go new file mode 100644 index 000000000..3bdb4a45c --- /dev/null +++ b/vendor/github.com/jesseduffield/gocui/scrollbar.go @@ -0,0 +1,33 @@ +package gocui + +import "math" + +// returns start and height of scrollbar +// `max` is the maximum possible value of `position` +func calcScrollbar(listSize int, pageSize int, position int, scrollAreaSize int) (int, int) { + height := calcScrollbarHeight(listSize, pageSize, scrollAreaSize) + // assume we can't scroll past the last item + maxPosition := listSize - pageSize + if maxPosition <= 0 { + return 0, height + } + if position == maxPosition { + return scrollAreaSize - height, height + } + // we only want to show the scrollbar at the top or bottom positions if we're at the end. Hence the .Ceil (for moving the scrollbar once we scroll down) and the -1 (for pretending there's a smaller range than we actually have, with the above condition ensuring we snap to the bottom once we're at the end of the list) + start := int(math.Ceil(((float64(position) / float64(maxPosition)) * float64(scrollAreaSize-height-1)))) + return start, height +} + +func calcScrollbarHeight(listSize int, pageSize int, scrollAreaSize int) int { + if pageSize >= listSize { + return scrollAreaSize + } + height := int((float64(pageSize) / float64(listSize)) * float64(scrollAreaSize)) + minHeight := 2 + if height < minHeight { + return minHeight + } + + return height +} diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index d2c2b75f1..8051cce72 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -158,6 +158,9 @@ type View struct { // something like '1 of 20' for a list view Footer string + + // if true, the user can scroll all the way past the last item until it appears at the top of the view + CanScrollPastBottom bool } // call this in the event of a view resize, or if you want to render new content @@ -1287,3 +1290,69 @@ func (v *View) OverwriteLines(y int, content string) { lines := strings.Replace(content, "\n", "\x1b[K\n", -1) v.writeString(lines) } + +func (v *View) ScrollUp(amount int) { + newOy := v.oy - amount + if newOy < 0 { + newOy = 0 + } + v.oy = newOy +} + +// ensures we don't scroll past the end of the view's content +func (v *View) ScrollDown(amount int) { + adjustedAmount := v.adjustDownwardScrollAmount(amount) + if adjustedAmount > 0 { + v.oy += adjustedAmount + } +} + +func (v *View) ScrollLeft(amount int) { + newOx := v.ox - amount + if newOx < 0 { + newOx = 0 + } + v.ox = newOx +} + +// not applying any limits to this +func (v *View) ScrollRight(amount int) { + v.ox += amount +} + +func (v *View) adjustDownwardScrollAmount(scrollHeight int) int { + _, oy := v.Origin() + y := oy + if !v.CanScrollPastBottom { + _, sy := v.Size() + y += sy + } + scrollableLines := v.ViewLinesHeight() - y + if scrollableLines < 0 { + return 0 + } + + margin := v.scrollMargin() + if scrollableLines-margin < scrollHeight { + scrollHeight = scrollableLines - margin + } + if oy+scrollHeight < 0 { + return 0 + } else { + return scrollHeight + } +} + +// scrollMargin is about how many lines must still appear if you scroll +// all the way down. We'll subtract this from the total amount of scrollable lines +func (v *View) scrollMargin() int { + if v.CanScrollPastBottom { + // Setting to 2 because of the newline at the end of the file that we're likely showing. + // If we want to scroll past bottom outside the context of reading a file's contents, + // we should make this into a field on the view to be configured by the client. + // For now we're hardcoding it. + return 2 + } else { + return 0 + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a7f378b45..68bceb9b0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -169,7 +169,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.20220415005542-2eb424ce3d0a +# github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67 ## explicit; go 1.12 github.com/jesseduffield/gocui # github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e