From c74448f00ddcfc8327bcd4f9d37281015899000f Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 25 Sep 2023 15:34:15 +1000 Subject: [PATCH] Don't select current search result when showing search status Previously there was no way to render a view's search status without also moving the cursor to the current search match. This caused issues where we wanted to display the status after leaving the view and coming back, or when beginning a new search from within the view. This commit separates the two use cases so we only move the cursor when we're actually selecting the next search match --- go.mod | 4 +- go.sum | 8 +-- pkg/gui/context/search_trait.go | 40 ++++++----- pkg/gui/controllers/helpers/search_helper.go | 3 +- pkg/gui/types/context.go | 1 + .../tests/filter_and_search/new_search.go | 39 ++++++++++ pkg/integration/tests/test_list.go | 1 + vendor/github.com/go-errors/errors/README.md | 1 + .../github.com/go-errors/errors/error_1_13.go | 26 ------- .../go-errors/errors/join_unwrap_1_20.go | 32 +++++++++ .../go-errors/errors/join_unwrap_backward.go | 71 +++++++++++++++++++ vendor/github.com/jesseduffield/gocui/view.go | 10 +-- vendor/modules.txt | 4 +- 13 files changed, 185 insertions(+), 55 deletions(-) create mode 100644 pkg/integration/tests/filter_and_search/new_search.go create mode 100644 vendor/github.com/go-errors/errors/join_unwrap_1_20.go create mode 100644 vendor/github.com/go-errors/errors/join_unwrap_backward.go diff --git a/go.mod b/go.mod index 2c609288d..b082b968b 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 github.com/creack/pty v1.1.11 github.com/fsmiamoto/git-todo-parser v0.0.5 - github.com/go-errors/errors v1.5.0 + github.com/go-errors/errors v1.5.1 github.com/gookit/color v1.4.2 github.com/imdario/mergo v0.3.11 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.20230909074155-fc7119a39341 + github.com/jesseduffield/gocui v0.3.1-0.20230925062444-7cd0d7e2a70a 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 5b4853b2c..deff61581 100644 --- a/go.sum +++ b/go.sum @@ -88,8 +88,8 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= -github.com/go-errors/errors v1.5.0 h1:/EuijeGOu7ckFxzhkj4CXJ8JaenxK7bKUxpPYqeLHqQ= -github.com/go-errors/errors v1.5.0/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM= @@ -179,8 +179,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.20230909074155-fc7119a39341 h1:COQ5uU+gFCQuE878cw0HcTSpKX+xLTu0/6lNTPJStG4= -github.com/jesseduffield/gocui v0.3.1-0.20230909074155-fc7119a39341/go.mod h1:trXE7RRGL2hTsv+Ntk+SHLtRobg9JE138n3Ug/X2Cf4= +github.com/jesseduffield/gocui v0.3.1-0.20230925062444-7cd0d7e2a70a h1:/Ifbvq4BWY5VKoyPnHIW5oRLiy00S3ebFtVIY2MlJm0= +github.com/jesseduffield/gocui v0.3.1-0.20230925062444-7cd0d7e2a70a/go.mod h1:trXE7RRGL2hTsv+Ntk+SHLtRobg9JE138n3Ug/X2Cf4= 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/context/search_trait.go b/pkg/gui/context/search_trait.go index 264c8217d..b8faf0757 100644 --- a/pkg/gui/context/search_trait.go +++ b/pkg/gui/context/search_trait.go @@ -37,20 +37,32 @@ func (self *SearchTrait) ClearSearchString() { func (self *SearchTrait) IsSearchableContext() {} func (self *SearchTrait) onSelectItemWrapper(innerFunc func(int) error) func(int, int, int) error { + return func(selectedLineIdx int, index int, total int) error { + self.RenderSearchStatus(index, total) + + if total != 0 { + if err := innerFunc(selectedLineIdx); err != nil { + return err + } + } + + return nil + } +} + +func (self *SearchTrait) RenderSearchStatus(index int, total int) { keybindingConfig := self.c.UserConfig.Keybinding - return func(y int, index int, total int) error { - if total == 0 { - self.c.SetViewContent( - self.c.Views().Search, - fmt.Sprintf( - self.c.Tr.NoMatchesFor, - self.searchString, - theme.OptionsFgColor.Sprintf(self.c.Tr.ExitSearchMode, keybindings.Label(keybindingConfig.Universal.Return)), - ), - ) - return nil - } + if total == 0 { + self.c.SetViewContent( + self.c.Views().Search, + fmt.Sprintf( + self.c.Tr.NoMatchesFor, + self.searchString, + theme.OptionsFgColor.Sprintf(self.c.Tr.ExitSearchMode, keybindings.Label(keybindingConfig.Universal.Return)), + ), + ) + } else { self.c.SetViewContent( self.c.Views().Search, fmt.Sprintf( @@ -66,10 +78,6 @@ func (self *SearchTrait) onSelectItemWrapper(innerFunc func(int) error) func(int ), ), ) - if err := innerFunc(y); err != nil { - return err - } - return nil } } diff --git a/pkg/gui/controllers/helpers/search_helper.go b/pkg/gui/controllers/helpers/search_helper.go index 8764337b1..c036bda3d 100644 --- a/pkg/gui/controllers/helpers/search_helper.go +++ b/pkg/gui/controllers/helpers/search_helper.go @@ -84,7 +84,8 @@ func (self *SearchHelper) DisplaySearchStatus(context types.ISearchableContext) state.Context = context self.searchPrefixView().SetContent(self.c.Tr.SearchPrefix) - _ = context.GetView().SelectCurrentSearchResult() + index, totalCount := context.GetView().GetSearchStatus() + context.RenderSearchStatus(index, totalCount) } func (self *SearchHelper) searchState() *types.SearchState { diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index fbc38df82..df16cf2bd 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -115,6 +115,7 @@ type ISearchableContext interface { ClearSearchString() IsSearching() bool IsSearchableContext() + RenderSearchStatus(int, int) } type DiffableContext interface { diff --git a/pkg/integration/tests/filter_and_search/new_search.go b/pkg/integration/tests/filter_and_search/new_search.go new file mode 100644 index 000000000..9186dc085 --- /dev/null +++ b/pkg/integration/tests/filter_and_search/new_search.go @@ -0,0 +1,39 @@ +package filter_and_search + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +// This is a regression test to ensure https://github.com/jesseduffield/lazygit/issues/2971 +// doesn't happen again + +var NewSearch = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Start a new search and verify the search begins from the current cursor position, not from the current search match", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + // need to create some branches, each with their own commits + shell.EmptyCommit("Add foo") + shell.EmptyCommit("Remove foo") + shell.EmptyCommit("Add bar") + shell.EmptyCommit("Remove bar") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Commits(). + Focus(). + Lines( + Contains(`Remove bar`).IsSelected(), + Contains(`Add bar`), + Contains(`Remove foo`), + Contains(`Add foo`), + ). + FilterOrSearch("Add"). + SelectedLine(Contains(`Add bar`)). + SelectPreviousItem(). + SelectedLine(Contains(`Remove bar`)). + FilterOrSearch("Remove"). + SelectedLine(Contains(`Remove bar`)) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index f3b57a5dd..89019719a 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -134,6 +134,7 @@ var tests = []*components.IntegrationTest{ filter_and_search.FilterSearchHistory, filter_and_search.NestedFilter, filter_and_search.NestedFilterTransient, + filter_and_search.NewSearch, filter_by_path.CliArg, filter_by_path.SelectFile, filter_by_path.TypeFile, diff --git a/vendor/github.com/go-errors/errors/README.md b/vendor/github.com/go-errors/errors/README.md index 1ec92329e..558bc883e 100644 --- a/vendor/github.com/go-errors/errors/README.md +++ b/vendor/github.com/go-errors/errors/README.md @@ -81,3 +81,4 @@ This package is licensed under the MIT license, see LICENSE.MIT for details. * v1.4.1 no code change, but now without an unnecessary cover.out file. * v1.4.2 performance improvement to ErrorStack() to avoid unnecessary work https://github.com/go-errors/errors/pull/40 * v1.5.0 add errors.Join() and errors.Unwrap() copying the stdlib https://github.com/go-errors/errors/pull/40 +* v1.5.1 fix build on go1.13..go1.19 (broken by adding Join and Unwrap with wrong build constraints) diff --git a/vendor/github.com/go-errors/errors/error_1_13.go b/vendor/github.com/go-errors/errors/error_1_13.go index 91e709caa..34ab3e00e 100644 --- a/vendor/github.com/go-errors/errors/error_1_13.go +++ b/vendor/github.com/go-errors/errors/error_1_13.go @@ -33,29 +33,3 @@ func Is(e error, original error) bool { return false } - -// Join returns an error that wraps the given errors. -// Any nil error values are discarded. -// Join returns nil if every value in errs is nil. -// The error formats as the concatenation of the strings obtained -// by calling the Error method of each element of errs, with a newline -// between each string. -// -// A non-nil error returned by Join implements the Unwrap() []error method. -// -// For more information see stdlib errors.Join. -func Join(errs ...error) error { - return baseErrors.Join(errs...) -} - -// Unwrap returns the result of calling the Unwrap method on err, if err's -// type contains an Unwrap method returning error. -// Otherwise, Unwrap returns nil. -// -// Unwrap only calls a method of the form "Unwrap() error". -// In particular Unwrap does not unwrap errors returned by [Join]. -// -// For more information see stdlib errors.Unwrap. -func Unwrap(err error) error { - return baseErrors.Unwrap(err) -} diff --git a/vendor/github.com/go-errors/errors/join_unwrap_1_20.go b/vendor/github.com/go-errors/errors/join_unwrap_1_20.go new file mode 100644 index 000000000..44df35ece --- /dev/null +++ b/vendor/github.com/go-errors/errors/join_unwrap_1_20.go @@ -0,0 +1,32 @@ +//go:build go1.20 +// +build go1.20 + +package errors + +import baseErrors "errors" + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if every value in errs is nil. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +// +// A non-nil error returned by Join implements the Unwrap() []error method. +// +// For more information see stdlib errors.Join. +func Join(errs ...error) error { + return baseErrors.Join(errs...) +} + +// Unwrap returns the result of calling the Unwrap method on err, if err's +// type contains an Unwrap method returning error. +// Otherwise, Unwrap returns nil. +// +// Unwrap only calls a method of the form "Unwrap() error". +// In particular Unwrap does not unwrap errors returned by [Join]. +// +// For more information see stdlib errors.Unwrap. +func Unwrap(err error) error { + return baseErrors.Unwrap(err) +} diff --git a/vendor/github.com/go-errors/errors/join_unwrap_backward.go b/vendor/github.com/go-errors/errors/join_unwrap_backward.go new file mode 100644 index 000000000..50c766976 --- /dev/null +++ b/vendor/github.com/go-errors/errors/join_unwrap_backward.go @@ -0,0 +1,71 @@ +//go:build !go1.20 +// +build !go1.20 + +package errors + +// Disclaimer: functions Join and Unwrap are copied from the stdlib errors +// package v1.21.0. + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if every value in errs is nil. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +// +// A non-nil error returned by Join implements the Unwrap() []error method. +func Join(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + var b []byte + for i, err := range e.errs { + if i > 0 { + b = append(b, '\n') + } + b = append(b, err.Error()...) + } + return string(b) +} + +func (e *joinError) Unwrap() []error { + return e.errs +} + +// Unwrap returns the result of calling the Unwrap method on err, if err's +// type contains an Unwrap method returning error. +// Otherwise, Unwrap returns nil. +// +// Unwrap only calls a method of the form "Unwrap() error". +// In particular Unwrap does not unwrap errors returned by [Join]. +func Unwrap(err error) error { + u, ok := err.(interface { + Unwrap() error + }) + if !ok { + return nil + } + return u.Unwrap() +} diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 707c32137..c907c1732 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -211,10 +211,6 @@ func (v *View) gotoPreviousMatch() error { return v.SelectSearchResult(v.searcher.currentSearchIndex) } -func (v *View) SelectCurrentSearchResult() error { - return v.SelectSearchResult(v.searcher.currentSearchIndex) -} - func (v *View) SelectSearchResult(index int) error { itemCount := len(v.searcher.searchPositions) if itemCount == 0 { @@ -225,6 +221,7 @@ func (v *View) SelectSearchResult(index int) error { } y := v.searcher.searchPositions[index].y + v.FocusPoint(v.ox, y) if v.searcher.onSelectItem != nil { return v.searcher.onSelectItem(y, index, itemCount) @@ -232,6 +229,11 @@ func (v *View) SelectSearchResult(index int) error { return nil } +// Returns , +func (v *View) GetSearchStatus() (int, int) { + return v.searcher.currentSearchIndex, len(v.searcher.searchPositions) +} + func (v *View) Search(str string) error { v.writeMutex.Lock() v.searcher.search(str) diff --git a/vendor/modules.txt b/vendor/modules.txt index 82ad517a3..fe2248d26 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -33,7 +33,7 @@ github.com/fsmiamoto/git-todo-parser/todo # github.com/gdamore/encoding v1.0.0 ## explicit; go 1.9 github.com/gdamore/encoding -# github.com/go-errors/errors v1.5.0 +# github.com/go-errors/errors v1.5.1 ## explicit; go 1.14 github.com/go-errors/errors # github.com/go-git/gcfg v1.5.0 @@ -124,7 +124,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.20230909074155-fc7119a39341 +# github.com/jesseduffield/gocui v0.3.1-0.20230925062444-7cd0d7e2a70a ## explicit; go 1.12 github.com/jesseduffield/gocui # github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10