summaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2024-06-23 11:48:00 +0200
committerGitHub <noreply@github.com>2024-06-23 11:48:00 +0200
commit5a5cd849d18779ff8f2d057a70550fa737a238fc (patch)
treee9092175bcdac6fbd3df73fffb9c72fe37d54ffa /pkg
parent3af545daf7cf6458e8efd324012047ce688f08e6 (diff)
parent6a6316cfb632a28627e067c8a4d30786acffee38 (diff)
Search the model instead of the view in the commits panel (#3642)
- **PR Description** This makes it possible to search the model data instead of the view when pressing `/`, and uses this for the commits view. This is mainly a preparation for #2533 which requires it, but it is also useful on its own, because it makes it possible to search for full commit hashes. It will highlight the abbreviated hash in that case. - **Please check if the PR fulfills these requirements** * [x] Cheatsheets are up-to-date (run `go generate ./...`) * [x] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting)) * [ ] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide) * [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation)) * [ ] Docs have been updated if necessary * [x] You've read through your own file changes for silly mistakes etc
Diffstat (limited to 'pkg')
-rw-r--r--pkg/gui/context/commit_files_context.go15
-rw-r--r--pkg/gui/context/list_context_trait.go2
-rw-r--r--pkg/gui/context/list_renderer.go6
-rw-r--r--pkg/gui/context/local_commits_context.go31
-rw-r--r--pkg/gui/context/patch_explorer_context.go9
-rw-r--r--pkg/gui/context/sub_commits_context.go17
-rw-r--r--pkg/gui/context/working_tree_context.go15
-rw-r--r--pkg/gui/controllers/helpers/refresh_helper.go10
-rw-r--r--pkg/gui/controllers/helpers/search_helper.go40
-rw-r--r--pkg/gui/types/context.go4
-rw-r--r--pkg/integration/tests/commit/search.go14
-rw-r--r--pkg/integration/tests/staging/search.go2
12 files changed, 124 insertions, 41 deletions
diff --git a/pkg/gui/context/commit_files_context.go b/pkg/gui/context/commit_files_context.go
index 7af968fb7..b56798fea 100644
--- a/pkg/gui/context/commit_files_context.go
+++ b/pkg/gui/context/commit_files_context.go
@@ -1,6 +1,7 @@
package context
import (
+ "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
@@ -18,8 +19,9 @@ type CommitFilesContext struct {
}
var (
- _ types.IListContext = (*CommitFilesContext)(nil)
- _ types.DiffableContext = (*CommitFilesContext)(nil)
+ _ types.IListContext = (*CommitFilesContext)(nil)
+ _ types.DiffableContext = (*CommitFilesContext)(nil)
+ _ types.ISearchableContext = (*CommitFilesContext)(nil)
)
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
@@ -64,10 +66,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
},
}
- ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
- ctx.GetList().SetSelection(selectedLineIdx)
- return ctx.HandleFocus(types.OnFocusOpts{})
- }))
+ ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(ctx.OnSearchSelect))
return ctx
}
@@ -75,3 +74,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
func (self *CommitFilesContext) GetDiffTerminals() []string {
return []string{self.GetRef().RefName()}
}
+
+func (self *CommitFilesContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
+ return nil
+}
diff --git a/pkg/gui/context/list_context_trait.go b/pkg/gui/context/list_context_trait.go
index eb738e332..c0e5ca04f 100644
--- a/pkg/gui/context/list_context_trait.go
+++ b/pkg/gui/context/list_context_trait.go
@@ -102,7 +102,7 @@ func (self *ListContextTrait) HandleRender() error {
}
func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error {
- self.GetList().SetSelection(selectedLineIdx)
+ self.GetList().SetSelection(self.ViewIndexToModelIndex(selectedLineIdx))
return self.HandleFocus(types.OnFocusOpts{})
}
diff --git a/pkg/gui/context/list_renderer.go b/pkg/gui/context/list_renderer.go
index f29407055..b89eccf8b 100644
--- a/pkg/gui/context/list_renderer.go
+++ b/pkg/gui/context/list_renderer.go
@@ -35,6 +35,7 @@ type ListRenderer struct {
numNonModelItems int
viewIndicesByModelIndex []int
modelIndicesByViewIndex []int
+ columnPositions []int
}
func (self *ListRenderer) GetList() types.IList {
@@ -59,6 +60,10 @@ func (self *ListRenderer) ViewIndexToModelIndex(viewIndex int) int {
return viewIndex
}
+func (self *ListRenderer) ColumnPositions() []int {
+ return self.columnPositions
+}
+
// startIdx and endIdx are view indices, not model indices. If you want to
// render the whole list, pass -1 for both.
func (self *ListRenderer) renderLines(startIdx int, endIdx int) string {
@@ -87,6 +92,7 @@ func (self *ListRenderer) renderLines(startIdx int, endIdx int) string {
lines, columnPositions := utils.RenderDisplayStrings(
self.getDisplayStrings(startModelIdx, endModelIdx),
columnAlignments)
+ self.columnPositions = columnPositions
lines = self.insertNonModelItems(nonModelItems, endIdx, startIdx, lines, columnPositions)
return strings.Join(lines, "\n")
}
diff --git a/pkg/gui/context/local_commits_context.go b/pkg/gui/context/local_commits_context.go
index d3fc6e240..c02fa3fe6 100644
--- a/pkg/gui/context/local_commits_context.go
+++ b/pkg/gui/context/local_commits_context.go
@@ -2,8 +2,10 @@ package context
import (
"log"
+ "strings"
"time"
+ "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
@@ -18,8 +20,9 @@ type LocalCommitsContext struct {
}
var (
- _ types.IListContext = (*LocalCommitsContext)(nil)
- _ types.DiffableContext = (*LocalCommitsContext)(nil)
+ _ types.IListContext = (*LocalCommitsContext)(nil)
+ _ types.DiffableContext = (*LocalCommitsContext)(nil)
+ _ types.ISearchableContext = (*LocalCommitsContext)(nil)
)
func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
@@ -85,10 +88,7 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
},
}
- ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
- ctx.GetList().SetSelection(selectedLineIdx)
- return ctx.HandleFocus(types.OnFocusOpts{})
- }))
+ ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(ctx.OnSearchSelect))
return ctx
}
@@ -155,6 +155,10 @@ func (self *LocalCommitsContext) GetDiffTerminals() []string {
return []string{itemId}
}
+func (self *LocalCommitsContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
+ return searchModelCommits(caseSensitive, self.GetCommits(), self.ColumnPositions(), searchStr)
+}
+
func (self *LocalCommitsViewModel) SetLimitCommits(value bool) {
self.limitCommits = value
}
@@ -194,3 +198,18 @@ func shouldShowGraph(c *ContextCommon) bool {
log.Fatalf("Unknown value for git.log.showGraph: %s. Expected one of: 'always', 'never', 'when-maximised'", value)
return false
}
+
+func searchModelCommits(caseSensitive bool, commits []*models.Commit, columnPositions []int, searchStr string) []gocui.SearchPosition {
+ normalize := lo.Ternary(caseSensitive, func(s string) string { return s }, strings.ToLower)
+ return lo.FilterMap(commits, func(commit *models.Commit, idx int) (gocui.SearchPosition, bool) {
+ // The XStart and XEnd values are only used if the search string can't
+ // be found in the view. This can really only happen if the user is
+ // searching for a commit hash that is longer than the truncated hash
+ // that we render. So we just set the XStart and XEnd values to the
+ // start and end of the commit hash column, which is the second one.
+ result := gocui.SearchPosition{XStart: columnPositions[1], XEnd: columnPositions[2] - 1, Y: idx}
+ return result, strings.Contains(normalize(commit.Hash), searchStr) ||
+ strings.Contains(normalize(commit.Name), searchStr) ||
+ strings.Contains(normalize(commit.ExtraInfo), searchStr) // allow searching for tags
+ })
+}
diff --git a/pkg/gui/context/patch_explorer_context.go b/pkg/gui/context/patch_explorer_context.go
index 34f70e2c7..ee856c5d2 100644
--- a/pkg/gui/context/patch_explorer_context.go
+++ b/pkg/gui/context/patch_explorer_context.go
@@ -18,7 +18,10 @@ type PatchExplorerContext struct {
mutex *deadlock.Mutex
}
-var _ types.IPatchExplorerContext = (*PatchExplorerContext)(nil)
+var (
+ _ types.IPatchExplorerContext = (*PatchExplorerContext)(nil)
+ _ types.ISearchableContext = (*PatchExplorerContext)(nil)
+)
func NewPatchExplorerContext(
view *gocui.View,
@@ -139,3 +142,7 @@ func (self *PatchExplorerContext) NavigateTo(isFocused bool, selectedLineIdx int
func (self *PatchExplorerContext) GetMutex() *deadlock.Mutex {
return self.mutex
}
+
+func (self *PatchExplorerContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
+ return nil
+}
diff --git a/pkg/gui/context/sub_commits_context.go b/pkg/gui/context/sub_commits_context.go
index f540dba87..842b364b3 100644
--- a/pkg/gui/context/sub_commits_context.go
+++ b/pkg/gui/context/sub_commits_context.go
@@ -4,6 +4,7 @@ import (
"fmt"
"time"
+ "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
@@ -21,8 +22,9 @@ type SubCommitsContext struct {
}
var (
- _ types.IListContext = (*SubCommitsContext)(nil)
- _ types.DiffableContext = (*SubCommitsContext)(nil)
+ _ types.IListContext = (*SubCommitsContext)(nil)
+ _ types.DiffableContext = (*SubCommitsContext)(nil)
+ _ types.ISearchableContext = (*SubCommitsContext)(nil)
)
func NewSubCommitsContext(
@@ -73,8 +75,6 @@ func NewSubCommitsContext(
selectedCommitHash,
startIdx,
endIdx,
- // Don't show the graph in the left/right view; we'd like to, but
- // it's too complicated:
shouldShowGraph(c),
git_commands.NewNullBisectInfo(),
false,
@@ -133,10 +133,7 @@ func NewSubCommitsContext(
},
}
- ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
- ctx.GetList().SetSelection(selectedLineIdx)
- return ctx.HandleFocus(types.OnFocusOpts{})
- }))
+ ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(ctx.OnSearchSelect))
return ctx
}
@@ -204,3 +201,7 @@ func (self *SubCommitsContext) GetDiffTerminals() []string {
return []string{itemId}
}
+
+func (self *SubCommitsContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
+ return searchModelCommits(caseSensitive, self.GetCommits(), self.ColumnPositions(), searchStr)
+}
diff --git a/pkg/gui/context/working_tree_context.go b/pkg/gui/context/working_tree_context.go
index 6fa462cb1..76eb56d57 100644
--- a/pkg/gui/context/working_tree_context.go
+++ b/pkg/gui/context/working_tree_context.go
@@ -1,6 +1,7 @@
package context
import (
+ "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
@@ -15,7 +16,10 @@ type WorkingTreeContext struct {
*SearchTrait
}
-var _ types.IListContext = (*WorkingTreeContext)(nil)
+var (
+ _ types.IListContext = (*WorkingTreeContext)(nil)
+ _ types.ISearchableContext = (*WorkingTreeContext)(nil)
+)
func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
viewModel := filetree.NewFileTreeViewModel(
@@ -51,10 +55,11 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
},
}
- ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error {
- ctx.GetList().SetSelection(selectedLineIdx)
- return ctx.HandleFocus(types.OnFocusOpts{})
- }))
+ ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(ctx.OnSearchSelect))
return ctx
}
+
+func (self *WorkingTreeContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
+ return nil
+}
diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go
index 4872add08..21c20c646 100644
--- a/pkg/gui/controllers/helpers/refresh_helper.go
+++ b/pkg/gui/controllers/helpers/refresh_helper.go
@@ -759,6 +759,14 @@ func (self *RefreshHelper) refForLog() string {
}
func (self *RefreshHelper) refreshView(context types.Context) error {
+ // Re-applying the filter must be done before re-rendering the view, so that
+ // the filtered list model is up to date for rendering.
self.searchHelper.ReApplyFilter(context)
- return self.c.PostRefreshUpdate(context)
+
+ err := self.c.PostRefreshUpdate(context)
+
+ // Re-applying the search must be done after re-rendering the view though,
+ // so that the "x of y" status is shown correctly.
+ self.searchHelper.ReApplySearch(context)
+ return err
}
diff --git a/pkg/gui/controllers/helpers/search_helper.go b/pkg/gui/controllers/helpers/search_helper.go
index c31720949..2cffe418b 100644
--- a/pkg/gui/controllers/helpers/search_helper.go
+++ b/pkg/gui/controllers/helpers/search_helper.go
@@ -2,12 +2,14 @@ package helpers
import (
"fmt"
+ "strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/theme"
+ "github.com/jesseduffield/lazygit/pkg/utils"
)
// NOTE: this helper supports both filtering and searching. Filtering is when
@@ -156,17 +158,26 @@ func (self *SearchHelper) ConfirmSearch() error {
context.GetSearchHistory().Push(searchString)
}
- view := context.GetView()
-
if err := self.c.PopContext(); err != nil {
return err
}
- if err := view.Search(searchString); err != nil {
- return err
+ return context.GetView().Search(searchString, modelSearchResults(context))
+}
+
+func modelSearchResults(context types.ISearchableContext) []gocui.SearchPosition {
+ searchString := context.GetSearchString()
+
+ var normalizedSearchStr string
+ // if we have any uppercase characters we'll do a case-sensitive search
+ caseSensitive := utils.ContainsUppercase(searchString)
+ if caseSensitive {
+ normalizedSearchStr = searchString
+ } else {
+ normalizedSearchStr = strings.ToLower(searchString)
}
- return nil
+ return context.ModelSearchResults(normalizedSearchStr, caseSensitive)
}
func (self *SearchHelper) CancelPrompt() error {
@@ -239,6 +250,25 @@ func (self *SearchHelper) ReApplyFilter(context types.Context) {
}
}
+func (self *SearchHelper) ReApplySearch(ctx types.Context) {
+ // Reapply the search if the model has changed. This is needed for contexts
+ // that use the model for searching, to pass the new model search positions
+ // to the view.
+ searchableContext, ok := ctx.(types.ISearchableContext)
+ if ok {
+ ctx.GetView().UpdateSearchResults(searchableContext.GetSearchString(), modelSearchResults(searchableContext))
+
+ state := self.searchState()
+ if ctx == state.Context {
+ // Re-render the "x of y" search status, unless the search prompt is
+ // open for typing.
+ if self.c.CurrentContext().GetKey() != context.SEARCH_CONTEXT_KEY {
+ self.RenderSearchStatus(searchableContext)
+ }
+ }
+ }
+}
+
func (self *SearchHelper) RenderSearchStatus(c types.Context) {
if c.GetKey() == context.SEARCH_CONTEXT_KEY {
return
diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go
index 63c759eb6..b0e312c97 100644
--- a/pkg/gui/types/context.go
+++ b/pkg/gui/types/context.go
@@ -114,12 +114,16 @@ type ISearchableContext interface {
Context
ISearchHistoryContext
+ // These are all implemented by SearchTrait
SetSearchString(string)
GetSearchString() string
ClearSearchString()
IsSearching() bool
IsSearchableContext()
RenderSearchStatus(int, int)
+
+ // This must be implemented by each concrete context. Return nil if not searching the model.
+ ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition
}
type DiffableContext interface {
diff --git a/pkg/integration/tests/commit/search.go b/pkg/integration/tests/commit/search.go
index c0a9dfd0b..24754b517 100644
--- a/pkg/integration/tests/commit/search.go
+++ b/pkg/integration/tests/commit/search.go
@@ -31,7 +31,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
Type("two").
Confirm()
- t.Views().Search().Content(Contains("matches for 'two' (1 of 1)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'two' (1 of 1)"))
}).
Lines(
Contains("four"),
@@ -46,7 +46,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
Type("o").
Confirm()
- t.Views().Search().Content(Contains("matches for 'o' (2 of 3)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'o' (2 of 3)"))
}).
Lines(
Contains("four"),
@@ -56,7 +56,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
).
Press("n").
Tap(func() {
- t.Views().Search().Content(Contains("matches for 'o' (3 of 3)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'o' (3 of 3)"))
}).
Lines(
Contains("four"),
@@ -66,7 +66,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
).
Press("n").
Tap(func() {
- t.Views().Search().Content(Contains("matches for 'o' (1 of 3)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'o' (1 of 3)"))
}).
Lines(
Contains("four").IsSelected(),
@@ -76,7 +76,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
).
Press("n").
Tap(func() {
- t.Views().Search().Content(Contains("matches for 'o' (2 of 3)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'o' (2 of 3)"))
}).
Lines(
Contains("four"),
@@ -86,7 +86,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
).
Press("N").
Tap(func() {
- t.Views().Search().Content(Contains("matches for 'o' (1 of 3)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'o' (1 of 3)"))
}).
Lines(
Contains("four").IsSelected(),
@@ -96,7 +96,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
).
Press("N").
Tap(func() {
- t.Views().Search().Content(Contains("matches for 'o' (3 of 3)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'o' (3 of 3)"))
}).
Lines(
Contains("four"),
diff --git a/pkg/integration/tests/staging/search.go b/pkg/integration/tests/staging/search.go
index a2c8edd69..1f2c95777 100644
--- a/pkg/integration/tests/staging/search.go
+++ b/pkg/integration/tests/staging/search.go
@@ -29,7 +29,7 @@ var Search = NewIntegrationTest(NewIntegrationTestArgs{
Type("four").
Confirm()
- t.Views().Search().Content(Contains("matches for 'four' (1 of 1)"))
+ t.Views().Search().IsVisible().Content(Contains("matches for 'four' (1 of 1)"))
}).
SelectedLine(Contains("+four")). // stage the line
PressPrimaryAction().