mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Support filtering files
This commit is contained in:
		| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| type CommitFilesContext struct { | ||||
| 	*FilteredList[*models.CommitFile] | ||||
| 	*filetree.CommitFileTreeViewModel | ||||
| 	*ListContextTrait | ||||
| 	*DynamicTitleBuilder | ||||
| @@ -21,8 +22,13 @@ var ( | ||||
| ) | ||||
|  | ||||
| func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext { | ||||
| 	viewModel := filetree.NewCommitFileTreeViewModel( | ||||
| 	filteredList := NewFilteredList( | ||||
| 		func() []*models.CommitFile { return c.Model().CommitFiles }, | ||||
| 		func(file *models.CommitFile) []string { return []string{file.GetPath()} }, | ||||
| 	) | ||||
|  | ||||
| 	viewModel := filetree.NewCommitFileTreeViewModel( | ||||
| 		func() []*models.CommitFile { return filteredList.GetFilteredList() }, | ||||
| 		c.Log, | ||||
| 		c.UserConfig.Gui.ShowFileTree, | ||||
| 	) | ||||
| @@ -39,6 +45,7 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext { | ||||
| 	} | ||||
|  | ||||
| 	return &CommitFilesContext{ | ||||
| 		FilteredList:            filteredList, | ||||
| 		CommitFileTreeViewModel: viewModel, | ||||
| 		DynamicTitleBuilder:     NewDynamicTitleBuilder(c.Tr.CommitFilesDynamicTitle), | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| @@ -71,3 +78,17 @@ func (self *CommitFilesContext) GetSelectedItemId() string { | ||||
| func (self *CommitFilesContext) GetDiffTerminals() []string { | ||||
| 	return []string{self.GetRef().RefName()} | ||||
| } | ||||
|  | ||||
| // used for type switch | ||||
| func (self *CommitFilesContext) IsFilterableContext() {} | ||||
|  | ||||
| // TODO: see if we can just call SetTree() within HandleRender(). It doesn't seem | ||||
| // right that we need to imperatively refresh the view model like this | ||||
| func (self *CommitFilesContext) SetFilter(filter string) { | ||||
| 	self.FilteredList.SetFilter(filter) | ||||
| 	self.SetTree() | ||||
| } | ||||
|  | ||||
| func (self *CommitFilesContext) ClearFilter() { | ||||
| 	self.SetFilter("") | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,13 @@ type FilteredList[T any] struct { | ||||
| 	filter          string | ||||
| } | ||||
|  | ||||
| func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string) *FilteredList[T] { | ||||
| 	return &FilteredList[T]{ | ||||
| 		getList:         getList, | ||||
| 		getFilterFields: getFilterFields, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *FilteredList[T]) GetFilter() string { | ||||
| 	return self.filter | ||||
| } | ||||
| @@ -28,13 +35,14 @@ func (self *FilteredList[T]) ClearFilter() { | ||||
| 	self.SetFilter("") | ||||
| } | ||||
|  | ||||
| func (self *FilteredList[T]) GetList() []T { | ||||
| 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()) | ||||
| } | ||||
|   | ||||
| @@ -6,16 +6,13 @@ type FilteredListViewModel[T any] struct { | ||||
| } | ||||
|  | ||||
| func NewFilteredListViewModel[T any](getList func() []T, getFilterFields func(T) []string) *FilteredListViewModel[T] { | ||||
| 	filteredList := &FilteredList[T]{ | ||||
| 		getList:         getList, | ||||
| 		getFilterFields: getFilterFields, | ||||
| 	} | ||||
| 	filteredList := NewFilteredList(getList, getFilterFields) | ||||
|  | ||||
| 	self := &FilteredListViewModel[T]{ | ||||
| 		FilteredList: filteredList, | ||||
| 	} | ||||
|  | ||||
| 	listViewModel := NewListViewModel(filteredList.GetList) | ||||
| 	listViewModel := NewListViewModel(filteredList.GetFilteredList) | ||||
|  | ||||
| 	self.ListViewModel = listViewModel | ||||
|  | ||||
|   | ||||
| @@ -9,20 +9,30 @@ import ( | ||||
| ) | ||||
|  | ||||
| type WorkingTreeContext struct { | ||||
| 	*FilteredList[*models.File] | ||||
| 	*filetree.FileTreeViewModel | ||||
| 	*ListContextTrait | ||||
| } | ||||
|  | ||||
| var _ types.IListContext = (*WorkingTreeContext)(nil) | ||||
| var ( | ||||
| 	_ types.IListContext       = (*WorkingTreeContext)(nil) | ||||
| 	_ types.IFilterableContext = (*WorkingTreeContext)(nil) | ||||
| ) | ||||
|  | ||||
| func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext { | ||||
| 	viewModel := filetree.NewFileTreeViewModel( | ||||
| 	filteredList := NewFilteredList( | ||||
| 		func() []*models.File { return c.Model().Files }, | ||||
| 		func(file *models.File) []string { return []string{file.GetPath()} }, | ||||
| 	) | ||||
|  | ||||
| 	viewModel := filetree.NewFileTreeViewModel( | ||||
| 		func() []*models.File { return filteredList.GetFilteredList() }, | ||||
| 		c.Log, | ||||
| 		c.UserConfig.Gui.ShowFileTree, | ||||
| 	) | ||||
|  | ||||
| 	getDisplayStrings := func(startIdx int, length int) [][]string { | ||||
| 		c.Log.Warn("in get display strings") | ||||
| 		lines := presentation.RenderFileTree(viewModel, c.Modes().Diffing.Ref, c.Model().Submodules) | ||||
| 		return slices.Map(lines, func(line string) []string { | ||||
| 			return []string{line} | ||||
| @@ -30,6 +40,7 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext { | ||||
| 	} | ||||
|  | ||||
| 	return &WorkingTreeContext{ | ||||
| 		FilteredList:      filteredList, | ||||
| 		FileTreeViewModel: viewModel, | ||||
| 		ListContextTrait: &ListContextTrait{ | ||||
| 			Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{ | ||||
| @@ -54,3 +65,17 @@ func (self *WorkingTreeContext) GetSelectedItemId() string { | ||||
|  | ||||
| 	return item.ID() | ||||
| } | ||||
|  | ||||
| // used for type switch | ||||
| func (self *WorkingTreeContext) IsFilterableContext() {} | ||||
|  | ||||
| // TODO: see if we can just call SetTree() within HandleRender(). It doesn't seem | ||||
| // right that we need to imperatively refresh the view model like this | ||||
| func (self *WorkingTreeContext) SetFilter(filter string) { | ||||
| 	self.FilteredList.SetFilter(filter) | ||||
| 	self.SetTree() | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeContext) ClearFilter() { | ||||
| 	self.SetFilter("") | ||||
| } | ||||
|   | ||||
| @@ -658,7 +658,7 @@ func (self *FilesController) handleStatusFilterPressed() error { | ||||
| } | ||||
|  | ||||
| func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error { | ||||
| 	self.context().FileTreeViewModel.SetFilter(filter) | ||||
| 	self.context().FileTreeViewModel.SetStatusFilter(filter) | ||||
| 	return self.c.PostRefreshUpdate(self.context()) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -464,10 +464,10 @@ func (self *RefreshHelper) refreshStateFiles() error { | ||||
| 	// I'd prefer to maintain as little state as possible. | ||||
| 	if conflictFileCount > 0 { | ||||
| 		if fileTreeViewModel.GetFilter() == filetree.DisplayAll { | ||||
| 			fileTreeViewModel.SetFilter(filetree.DisplayConflicted) | ||||
| 			fileTreeViewModel.SetStatusFilter(filetree.DisplayConflicted) | ||||
| 		} | ||||
| 	} else if fileTreeViewModel.GetFilter() == filetree.DisplayConflicted { | ||||
| 		fileTreeViewModel.SetFilter(filetree.DisplayAll) | ||||
| 		fileTreeViewModel.SetStatusFilter(filetree.DisplayAll) | ||||
| 	} | ||||
|  | ||||
| 	self.c.Model().Files = files | ||||
|   | ||||
| @@ -34,7 +34,7 @@ type IFileTree interface { | ||||
| 	ITree[models.File] | ||||
|  | ||||
| 	FilterFiles(test func(*models.File) bool) []*models.File | ||||
| 	SetFilter(filter FileTreeDisplayFilter) | ||||
| 	SetStatusFilter(filter FileTreeDisplayFilter) | ||||
| 	Get(index int) *FileNode | ||||
| 	GetFile(path string) *models.File | ||||
| 	GetAllItems() []*FileNode | ||||
| @@ -91,7 +91,7 @@ func (self *FileTree) FilterFiles(test func(*models.File) bool) []*models.File { | ||||
| 	return slices.Filter(self.getFiles(), test) | ||||
| } | ||||
|  | ||||
| func (self *FileTree) SetFilter(filter FileTreeDisplayFilter) { | ||||
| func (self *FileTree) SetStatusFilter(filter FileTreeDisplayFilter) { | ||||
| 	self.filter = filter | ||||
| 	self.SetTree() | ||||
| } | ||||
| @@ -102,7 +102,7 @@ func (self *FileTree) ToggleShowTree() { | ||||
| } | ||||
|  | ||||
| func (self *FileTree) Get(index int) *FileNode { | ||||
| 	// need to traverse the three depth first until we get to the index. | ||||
| 	// need to traverse the tree depth first until we get to the index. | ||||
| 	return NewFileNode(self.tree.GetNodeAtIndex(index+1, self.collapsedPaths)) // ignoring root | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -26,8 +26,6 @@ type FileTreeViewModel struct { | ||||
|  | ||||
| var _ IFileTreeViewModel = &FileTreeViewModel{} | ||||
|  | ||||
| // how to tackle this? We could just filter down the list of files at a high point and then the rest will take care of itself. | ||||
|  | ||||
| func NewFileTreeViewModel(getFiles func() []*models.File, log *logrus.Entry, showTree bool) *FileTreeViewModel { | ||||
| 	fileTree := NewFileTree(getFiles, log, showTree) | ||||
| 	listCursor := traits.NewListCursor(fileTree) | ||||
| @@ -128,8 +126,8 @@ func (self *FileTreeViewModel) findNewSelectedIdx(prevNodes []*FileNode, currNod | ||||
| 	return -1 | ||||
| } | ||||
|  | ||||
| func (self *FileTreeViewModel) SetFilter(filter FileTreeDisplayFilter) { | ||||
| 	self.IFileTree.SetFilter(filter) | ||||
| func (self *FileTreeViewModel) SetStatusFilter(filter FileTreeDisplayFilter) { | ||||
| 	self.IFileTree.SetStatusFilter(filter) | ||||
| 	self.IListCursor.SetSelectedLineIdx(0) | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user