diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2023-07-03 12:57:11 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-03 12:57:11 +1000 |
commit | 1a36cb9f3fbcf07353b8ae73e8b055d2e0e1d794 (patch) | |
tree | f486f588c72676322c341cb9aae209a02cbf9c61 /pkg | |
parent | 2be4359e87bee6c737e19b02e4c7896fbc2ebf91 (diff) | |
parent | 5d982e1d70aa9a07645a5665130f2e499b7b56c8 (diff) |
View filtering (#2680)
Diffstat (limited to 'pkg')
81 files changed, 1975 insertions, 433 deletions
diff --git a/pkg/commands/models/commit_file.go b/pkg/commands/models/commit_file.go index 45b56d2dd..d05b5b3fd 100644 --- a/pkg/commands/models/commit_file.go +++ b/pkg/commands/models/commit_file.go @@ -23,3 +23,7 @@ func (f *CommitFile) Added() bool { func (f *CommitFile) Deleted() bool { return f.ChangeStatus == "D" } + +func (f *CommitFile) GetPath() string { + return f.Name +} diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index c3e4fc07f..8faff4326 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -60,15 +60,16 @@ type GuiConfig struct { } type ThemeConfig struct { - ActiveBorderColor []string `yaml:"activeBorderColor"` - InactiveBorderColor []string `yaml:"inactiveBorderColor"` - OptionsTextColor []string `yaml:"optionsTextColor"` - SelectedLineBgColor []string `yaml:"selectedLineBgColor"` - SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"` - CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"` - CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"` - UnstagedChangesColor []string `yaml:"unstagedChangesColor"` - DefaultFgColor []string `yaml:"defaultFgColor"` + ActiveBorderColor []string `yaml:"activeBorderColor"` + InactiveBorderColor []string `yaml:"inactiveBorderColor"` + SearchingActiveBorderColor []string `yaml:"searchingActiveBorderColor"` + OptionsTextColor []string `yaml:"optionsTextColor"` + SelectedLineBgColor []string `yaml:"selectedLineBgColor"` + SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"` + CherryPickedCommitBgColor []string `yaml:"cherryPickedCommitBgColor"` + CherryPickedCommitFgColor []string `yaml:"cherryPickedCommitFgColor"` + UnstagedChangesColor []string `yaml:"unstagedChangesColor"` + DefaultFgColor []string `yaml:"defaultFgColor"` } type CommitLengthConfig struct { @@ -409,15 +410,16 @@ func GetDefaultConfig() *UserConfig { TimeFormat: "02 Jan 06", ShortTimeFormat: time.Kitchen, Theme: ThemeConfig{ - ActiveBorderColor: []string{"green", "bold"}, - InactiveBorderColor: []string{"default"}, - OptionsTextColor: []string{"blue"}, - SelectedLineBgColor: []string{"blue"}, - SelectedRangeBgColor: []string{"blue"}, - CherryPickedCommitBgColor: []string{"cyan"}, - CherryPickedCommitFgColor: []string{"blue"}, - UnstagedChangesColor: []string{"red"}, - DefaultFgColor: []string{"default"}, + ActiveBorderColor: []string{"green", "bold"}, + SearchingActiveBorderColor: []string{"cyan", "bold"}, + InactiveBorderColor: []string{"default"}, + OptionsTextColor: []string{"blue"}, + SelectedLineBgColor: []string{"blue"}, + SelectedRangeBgColor: []string{"blue"}, + CherryPickedCommitBgColor: []string{"cyan"}, + CherryPickedCommitFgColor: []string{"blue"}, + UnstagedChangesColor: []string{"red"}, + DefaultFgColor: []string{"default"}, }, CommitLength: CommitLengthConfig{Show: true}, SkipNoStagedFilesWarning: false, diff --git a/pkg/gui/command_log_panel.go b/pkg/gui/command_log_panel.go index 0a5ccfae3..2faee3572 100644 --- a/pkg/gui/command_log_panel.go +++ b/pkg/gui/command_log_panel.go @@ -136,10 +136,6 @@ func (gui *Gui) getRandomTip() string { formattedKey(config.Universal.Return), ), fmt.Sprintf( - "To search for a string in your panel, press '%s'", - formattedKey(config.Universal.StartSearch), - ), - fmt.Sprintf( "You can page through the items of a panel using '%s' and '%s'", formattedKey(config.Universal.PrevPage), formattedKey(config.Universal.NextPage), diff --git a/pkg/gui/context.go b/pkg/gui/context.go index b55713f27..38e1212c7 100644 --- a/pkg/gui/context.go +++ b/pkg/gui/context.go @@ -200,9 +200,9 @@ func (self *ContextMgr) RemoveContexts(contextsToRemove []types.Context) error { func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error { view, _ := self.gui.c.GocuiGui().View(c.GetViewName()) - if view != nil && view.IsSearching() { - if err := self.gui.onSearchEscape(); err != nil { - return err + if opts.NewContextKey != context.SEARCH_CONTEXT_KEY { + if c.GetKind() == types.MAIN_CONTEXT || c.GetKind() == types.TEMPORARY_POPUP { + self.gui.helpers.Search.CancelSearchIfSearching(c) } } @@ -234,6 +234,8 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts) return err } + self.gui.helpers.Search.RenderSearchStatus(c) + desiredTitle := c.Title() if desiredTitle != "" { v.Title = desiredTitle @@ -326,6 +328,30 @@ func (self *ContextMgr) IsCurrent(c types.Context) bool { return self.Current().GetKey() == c.GetKey() } +func (self *ContextMgr) AllFilterable() []types.IFilterableContext { + var result []types.IFilterableContext + + for _, context := range self.allContexts.Flatten() { + if ctx, ok := context.(types.IFilterableContext); ok { + result = append(result, ctx) + } + } + + return result +} + +func (self *ContextMgr) AllSearchable() []types.ISearchableContext { + var result []types.ISearchableContext + + for _, context := range self.allContexts.Flatten() { + if ctx, ok := context.(types.ISearchableContext); ok { + result = append(result, ctx) + } + } + + return result +} + // all list contexts func (self *ContextMgr) AllList() []types.IListContext { var listContexts []types.IListContext diff --git a/pkg/gui/context/branches_context.go b/pkg/gui/context/branches_context.go index c2463ad20..497b3a2c4 100644 --- a/pkg/gui/context/branches_context.go +++ b/pkg/gui/context/branches_context.go @@ -7,7 +7,7 @@ import ( ) type BranchesContext struct { - *BasicViewModel[*models.Branch] + *FilteredListViewModel[*models.Branch] *ListContextTrait } @@ -17,11 +17,16 @@ var ( ) func NewBranchesContext(c *ContextCommon) *BranchesContext { - viewModel := NewBasicViewModel(func() []*models.Branch { return c.Model().Branches }) + viewModel := NewFilteredListViewModel( + func() []*models.Branch { return c.Model().Branches }, + func(branch *models.Branch) []string { + return []string{branch.Name} + }, + ) getDisplayStrings := func(startIdx int, length int) [][]string { return presentation.GetBranchListDisplayStrings( - c.Model().Branches, + viewModel.GetItems(), c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL, c.Modes().Diffing.Ref, c.Tr, @@ -30,7 +35,7 @@ func NewBranchesContext(c *ContextCommon) *BranchesContext { } self := &BranchesContext{ - BasicViewModel: viewModel, + FilteredListViewModel: viewModel, ListContextTrait: &ListContextTrait{ Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ View: c.Views().Branches, diff --git a/pkg/gui/context/commit_files_context.go b/pkg/gui/context/commit_files_context.go index 96b6f2fcf..5cb11d9cc 100644 --- a/pkg/gui/context/commit_files_context.go +++ b/pkg/gui/context/commit_files_context.go @@ -13,6 +13,7 @@ type CommitFilesContext struct { *filetree.CommitFileTreeViewModel *ListContextTrait *DynamicTitleBuilder + *SearchTrait } var ( @@ -38,9 +39,10 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext { }) } - return &CommitFilesContext{ + ctx := &CommitFilesContext{ CommitFileTreeViewModel: viewModel, DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.CommitFilesDynamicTitle), + SearchTrait: NewSearchTrait(c), ListContextTrait: &ListContextTrait{ Context: NewSimpleContext( NewBaseContext(NewBaseContextOpts{ @@ -57,6 +59,13 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext { c: c, }, } + + ctx.GetView().SetOnSelectItem(ctx.SearchTrait.onSelectItemWrapper(func(selectedLineIdx int) error { + ctx.GetList().SetSelectedLineIdx(selectedLineIdx) + return ctx.HandleFocus(types.OnFocusOpts{}) + })) + + return ctx } func (self *CommitFilesContext) GetSelectedItemId() string { diff --git a/pkg/gui/context/filtered_list.go b/pkg/gui/context/filtered_list.go new file mode 100644 index 000000000..298d3d615 --- /dev/null +++ b/pkg/gui/context/filtered_list.go @@ -0,0 +1,93 @@ +package context + +import ( + "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/sasha-s/go-deadlock" +) + +type FilteredList[T any] struct { + filteredIndices []int // if nil, we are not filtering + + getList func() []T + getFilterFields func(T) []string + filter string + + mutex *deadlock.Mutex +} + +func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string) *FilteredList[T] { + return &FilteredList[T]{ + getList: getList, + getFilterFields: getFilterFields, + mutex: &deadlock.Mutex{}, + } +} + +func (self *FilteredList[T]) GetFilter() string { + return self.filter +} + +func (self *FilteredList[T]) SetFilter(filter string) { + self.filter = filter + + self.applyFilter() +} + +func (self *FilteredList[T]) ClearFilter() { + self.SetFilter("") +} + +func (self *FilteredList[T]) IsFiltering() bool { + return self.filter != "" +} + +func (self *FilteredList[T]) GetFilteredList() []T { + if self.filteredIndices == nil { + return self.getList() + } + return utils.ValuesAtIndices(self.getList(), self.filteredIndices) +} + +// TODO: update to just 'Len' +func (self *FilteredList[T]) UnfilteredLen() int { + return len(self.getList()) +} + +func (self *FilteredList[T]) applyFilter() { + self.mutex.Lock() + defer self.mutex.Unlock() + + if self.filter == "" { + self.filteredIndices = nil + } else { + self.filteredIndices = []int{} + for i, item := range self.getList() { + for _, field := range self.getFilterFields(item) { + if self.match(field, self.filter) { + self.filteredIndices = append(self.filteredIndices, i) + break + } + } + } + } +} + +func (self *FilteredList[T]) match(haystack string, needle string) bool { + return utils.CaseAwareContains(haystack, needle) +} + +func (self *FilteredList[T]) UnfilteredIndex(index int) int { + self.mutex.Lock() + defer self.mutex.Unlock() + + if self.filteredIndices == nil { + return index + } |