mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 15:46:26 -04:00
Filter file views rather than search
Change working tree files and commit files panels to use filtering (reducing the list) instead of search (highlighting matches). This matches the behavior of other filterable views. The text filter matches against the full file path, not just the filename, which is more useful for navigating large directory trees. When toggling a directory for a custom patch while a text filter is active, all files in the directory are included (not just the visible filtered ones), consistent with how staging a directory in the files panel works. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3db8175bb5
commit
75488f1041
|
|
@ -71,7 +71,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Commit summary
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Input prompt
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
|||
| `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます |
|
||||
| `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します |
|
||||
| `` 0 `` | メインビューにフォーカス | |
|
||||
| `` / `` | 現在のビューをテキストで検索 | |
|
||||
| `` / `` | 現在のビューをテキストでフィルタリング | |
|
||||
|
||||
## コミット概要
|
||||
|
||||
|
|
@ -242,7 +242,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
|||
| `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます |
|
||||
| `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します |
|
||||
| `` 0 `` | メインビューにフォーカス | |
|
||||
| `` / `` | 現在のビューをテキストで検索 | |
|
||||
| `` / `` | 現在のビューをテキストでフィルタリング | |
|
||||
|
||||
## メインパネル(ステージング)
|
||||
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | 검색 시작 | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## 커밋메시지
|
||||
|
||||
|
|
@ -405,7 +405,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | 검색 시작 | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## 확인 패널
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Start met zoeken | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Bevestigingspaneel
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Start met zoeken | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Commits
|
||||
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Szukaj w bieżącym widoku po tekście | |
|
||||
| `` / `` | Filtruj bieżący widok po tekście | |
|
||||
|
||||
## Pliki commita
|
||||
|
||||
|
|
@ -280,7 +280,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Szukaj w bieżącym widoku po tekście | |
|
||||
| `` / `` | Filtruj bieżący widok po tekście | |
|
||||
|
||||
## Podsumowanie commita
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
|
||||
| `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Branches locais
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||
| `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos |
|
||||
| `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Search the current view by text | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Commits
|
||||
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ _Связки клавиш_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Найти | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Статус
|
||||
|
||||
|
|
@ -399,7 +399,7 @@ _Связки клавиш_
|
|||
| `` - `` | Collapse all files | Collapse all directories in the files tree |
|
||||
| `` = `` | Expand all files | Expand all directories in the file tree |
|
||||
| `` 0 `` | Focus main view | |
|
||||
| `` / `` | Найти | |
|
||||
| `` / `` | Filter the current view by text | |
|
||||
|
||||
## Хранилище
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
|||
| `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
|
||||
| `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
|
||||
| `` 0 `` | 聚焦主视图 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 文件
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
|||
| `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 |
|
||||
| `` = `` | 展开全部文件 | 展开文件树中的全部目录 |
|
||||
| `` 0 `` | 聚焦主视图 | |
|
||||
| `` / `` | 开始搜索 | |
|
||||
| `` / `` | 通过文本过滤当前视图 | |
|
||||
|
||||
## 本地分支
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package context
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
|
|
@ -17,13 +16,12 @@ type CommitFilesContext struct {
|
|||
*filetree.CommitFileTreeViewModel
|
||||
*ListContextTrait
|
||||
*DynamicTitleBuilder
|
||||
*SearchTrait
|
||||
}
|
||||
|
||||
var (
|
||||
_ types.IListContext = (*CommitFilesContext)(nil)
|
||||
_ types.DiffableContext = (*CommitFilesContext)(nil)
|
||||
_ types.ISearchableContext = (*CommitFilesContext)(nil)
|
||||
_ types.IFilterableContext = (*CommitFilesContext)(nil)
|
||||
)
|
||||
|
||||
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
|
|
@ -48,7 +46,6 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
|||
ctx := &CommitFilesContext{
|
||||
CommitFileTreeViewModel: viewModel,
|
||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.CommitFilesDynamicTitle),
|
||||
SearchTrait: NewSearchTrait(c),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
|
|
@ -68,9 +65,6 @@ func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
|||
},
|
||||
}
|
||||
|
||||
ctx.GetView().SetRenderSearchStatus(ctx.SearchTrait.RenderSearchStatus)
|
||||
ctx.GetView().SetOnSelectItem(ctx.OnSearchSelect)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
|
@ -93,10 +87,6 @@ func (self *CommitFilesContext) GetFromAndToForDiff() (string, string) {
|
|||
return ref.ParentRefName(), ref.RefName()
|
||||
}
|
||||
|
||||
func (self *CommitFilesContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CommitFilesContext) ReInit(ref models.Ref, refRange *types.RefRange) {
|
||||
self.SetRef(ref)
|
||||
self.SetRefRange(refRange)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
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"
|
||||
|
|
@ -13,12 +12,11 @@ import (
|
|||
type WorkingTreeContext struct {
|
||||
*filetree.FileTreeViewModel
|
||||
*ListContextTrait
|
||||
*SearchTrait
|
||||
}
|
||||
|
||||
var (
|
||||
_ types.IListContext = (*WorkingTreeContext)(nil)
|
||||
_ types.ISearchableContext = (*WorkingTreeContext)(nil)
|
||||
_ types.IFilterableContext = (*WorkingTreeContext)(nil)
|
||||
)
|
||||
|
||||
func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||
|
|
@ -38,7 +36,6 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
|||
}
|
||||
|
||||
ctx := &WorkingTreeContext{
|
||||
SearchTrait: NewSearchTrait(c),
|
||||
FileTreeViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
|
|
@ -56,12 +53,5 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
|||
},
|
||||
}
|
||||
|
||||
ctx.GetView().SetRenderSearchStatus(ctx.SearchTrait.RenderSearchStatus)
|
||||
ctx.GetView().SetOnSelectItem(ctx.OnSearchSelect)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (self *WorkingTreeContext) ModelSearchResults(searchStr string, caseSensitive bool) []gocui.SearchPosition {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -400,12 +400,15 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm
|
|||
|
||||
selectedNodes = normalisedSelectedCommitFileNodes(selectedNodes)
|
||||
|
||||
// Collect all files to operate on. For directory nodes, this
|
||||
// includes all files under the directory, not just the ones
|
||||
// visible after filtering.
|
||||
filesToProcess := self.collectFilesForNodes(selectedNodes)
|
||||
|
||||
// Find if any file in the selection is unselected or partially added
|
||||
adding := lo.SomeBy(selectedNodes, func(node *filetree.CommitFileNode) bool {
|
||||
return node.SomeFile(func(file *models.CommitFile) bool {
|
||||
fileStatus := self.c.Git().Patch.PatchBuilder.GetFileStatus(file.Path, self.context().GetRef().RefName())
|
||||
return fileStatus == patch.PART || fileStatus == patch.UNSELECTED
|
||||
})
|
||||
adding := lo.SomeBy(filesToProcess, func(file *models.CommitFile) bool {
|
||||
fileStatus := self.c.Git().Patch.PatchBuilder.GetFileStatus(file.Path, self.context().GetRef().RefName())
|
||||
return fileStatus == patch.PART || fileStatus == patch.UNSELECTED
|
||||
})
|
||||
|
||||
patchOperationFunction := self.c.Git().Patch.PatchBuilder.RemoveFile
|
||||
|
|
@ -414,11 +417,8 @@ func (self *CommitFilesController) toggleForPatch(selectedNodes []*filetree.Comm
|
|||
patchOperationFunction = self.c.Git().Patch.PatchBuilder.AddFileWhole
|
||||
}
|
||||
|
||||
for _, node := range selectedNodes {
|
||||
err := node.ForEachFile(func(file *models.CommitFile) error {
|
||||
return patchOperationFunction(file.Path)
|
||||
})
|
||||
if err != nil {
|
||||
for _, file := range filesToProcess {
|
||||
if err := patchOperationFunction(file.Path); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -570,6 +570,30 @@ func isDescendentOfSelectedCommitFileNodes(node *filetree.CommitFileNode, select
|
|||
return false
|
||||
}
|
||||
|
||||
// collectFilesForNodes returns all commit files that should be operated on
|
||||
// for the given nodes. For directory nodes, this includes all files under
|
||||
// the directory from the full unfiltered list, so that toggling a directory
|
||||
// for a patch always affects all files regardless of any active text filter.
|
||||
func (self *CommitFilesController) collectFilesForNodes(nodes []*filetree.CommitFileNode) []*models.CommitFile {
|
||||
allCommitFiles := self.context().GetAllFiles()
|
||||
result := []*models.CommitFile{}
|
||||
|
||||
for _, node := range nodes {
|
||||
if node.IsFile() {
|
||||
result = append(result, node.File)
|
||||
} else {
|
||||
dirPath := node.GetPath()
|
||||
for _, file := range allCommitFiles {
|
||||
if dirPath == "" || strings.HasPrefix(file.Path, dirPath+"/") {
|
||||
result = append(result, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) isInTreeMode() *types.DisabledReason {
|
||||
if !self.context().CommitFileTreeViewModel.InTreeMode() {
|
||||
return &types.DisabledReason{Text: self.c.Tr.DisabledInFlatView}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ func NewSearchHelper(
|
|||
func (self *SearchHelper) OpenFilterPrompt(context types.IFilterableContext) error {
|
||||
state := self.searchState()
|
||||
|
||||
state.PrevSearchIndex = -1
|
||||
|
||||
state.Context = context
|
||||
|
||||
self.searchPrefixView().SetContent(context.FilterPrefix(self.c.Tr))
|
||||
|
|
@ -238,7 +240,7 @@ func (self *SearchHelper) ReApplyFilter(context types.Context) {
|
|||
filterableContext, ok := context.(types.IFilterableContext)
|
||||
if ok {
|
||||
state := self.searchState()
|
||||
if context == state.Context {
|
||||
if context == state.Context && self.c.Context().Current().GetKey() == self.c.Contexts().Search.GetKey() {
|
||||
filterableContext.SetSelection(0)
|
||||
filterableContext.GetView().SetOriginY(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,12 +80,12 @@ func (self *SwitchToDiffFilesController) enter() error {
|
|||
}
|
||||
}
|
||||
|
||||
commitFilesContext.ClearFilter()
|
||||
commitFilesContext.ReInit(ref, refsRange)
|
||||
commitFilesContext.SetSelection(0)
|
||||
commitFilesContext.SetCanRebase(canRebase)
|
||||
commitFilesContext.SetParentContext(self.context)
|
||||
commitFilesContext.SetWindowName(self.context.GetWindowName())
|
||||
commitFilesContext.ClearSearchString()
|
||||
commitFilesContext.GetView().TitlePrefix = self.context.GetView().TitlePrefix
|
||||
|
||||
self.c.Refresh(types.RefreshOptions{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ type ICommitFileTree interface {
|
|||
GetAllItems() []*CommitFileNode
|
||||
GetAllFiles() []*models.CommitFile
|
||||
GetRoot() *CommitFileNode
|
||||
SetTextFilter(filter string, useFuzzySearch bool)
|
||||
GetTextFilter() string
|
||||
}
|
||||
|
||||
type CommitFileTree struct {
|
||||
|
|
@ -23,6 +25,8 @@ type CommitFileTree struct {
|
|||
showTree bool
|
||||
common *common.Common
|
||||
collapsedPaths *CollapsedPaths
|
||||
textFilter string
|
||||
useFuzzySearch bool
|
||||
}
|
||||
|
||||
func (self *CommitFileTree) CollapseAll() {
|
||||
|
|
@ -93,15 +97,34 @@ func (self *CommitFileTree) GetAllFiles() []*models.CommitFile {
|
|||
return self.getFiles()
|
||||
}
|
||||
|
||||
func (self *CommitFileTree) getFilesForDisplay() []*models.CommitFile {
|
||||
files := self.getFiles()
|
||||
if self.textFilter != "" {
|
||||
files = filterCommitFilesByText(files, self.textFilter, self.useFuzzySearch)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func (self *CommitFileTree) SetTree() {
|
||||
filesForDisplay := self.getFilesForDisplay()
|
||||
showRootItem := self.common.UserConfig().Gui.ShowRootItemInFileTree
|
||||
if self.showTree {
|
||||
self.tree = BuildTreeFromCommitFiles(self.getFiles(), showRootItem)
|
||||
self.tree = BuildTreeFromCommitFiles(filesForDisplay, showRootItem)
|
||||
} else {
|
||||
self.tree = BuildFlatTreeFromCommitFiles(self.getFiles(), showRootItem)
|
||||
self.tree = BuildFlatTreeFromCommitFiles(filesForDisplay, showRootItem)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitFileTree) SetTextFilter(filter string, useFuzzySearch bool) {
|
||||
self.textFilter = filter
|
||||
self.useFuzzySearch = useFuzzySearch
|
||||
self.SetTree()
|
||||
}
|
||||
|
||||
func (self *CommitFileTree) GetTextFilter() string {
|
||||
return self.textFilter
|
||||
}
|
||||
|
||||
func (self *CommitFileTree) IsCollapsed(path string) bool {
|
||||
return self.collapsedPaths.IsCollapsed(path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import (
|
|||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
|
|
@ -39,6 +41,8 @@ type CommitFileTreeViewModel struct {
|
|||
// we set this to true when you're viewing the files within the checked-out branch's commits.
|
||||
// If you're viewing the files of some random other branch we can't do any rebase stuff.
|
||||
canRebase bool
|
||||
|
||||
searchHistory *utils.HistoryBuffer[string]
|
||||
}
|
||||
|
||||
var _ ICommitFileTreeViewModel = &CommitFileTreeViewModel{}
|
||||
|
|
@ -52,6 +56,7 @@ func NewCommitFileTreeViewModel(getFiles func() []*models.CommitFile, common *co
|
|||
ref: nil,
|
||||
refRange: nil,
|
||||
canRebase: false,
|
||||
searchHistory: utils.NewHistoryBuffer[string](1000),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,3 +208,51 @@ func (self *CommitFileTreeViewModel) SelectPath(filepath string, showRootItem bo
|
|||
self.SetSelection(index)
|
||||
}
|
||||
}
|
||||
|
||||
// IFilterableContext methods
|
||||
|
||||
func (self *CommitFileTreeViewModel) SetFilter(filter string, useFuzzySearch bool) {
|
||||
self.ICommitFileTree.SetTextFilter(filter, useFuzzySearch)
|
||||
}
|
||||
|
||||
func (self *CommitFileTreeViewModel) GetFilter() string {
|
||||
return self.ICommitFileTree.GetTextFilter()
|
||||
}
|
||||
|
||||
func (self *CommitFileTreeViewModel) ClearFilter() {
|
||||
selectedNode := self.GetSelected()
|
||||
var selectedPath string
|
||||
if selectedNode != nil {
|
||||
selectedPath = selectedNode.GetInternalPath()
|
||||
}
|
||||
|
||||
self.ICommitFileTree.SetTextFilter("", false)
|
||||
|
||||
if selectedPath != "" {
|
||||
self.ExpandToPath(selectedPath)
|
||||
if idx, found := self.GetIndexForPath(selectedPath); found {
|
||||
self.SetSelection(idx)
|
||||
return
|
||||
}
|
||||
}
|
||||
self.ClampSelection()
|
||||
}
|
||||
|
||||
func (self *CommitFileTreeViewModel) ReApplyFilter(useFuzzySearch bool) {
|
||||
self.ICommitFileTree.SetTextFilter(self.ICommitFileTree.GetTextFilter(), useFuzzySearch)
|
||||
}
|
||||
|
||||
func (self *CommitFileTreeViewModel) IsFiltering() bool {
|
||||
return self.ICommitFileTree.GetTextFilter() != ""
|
||||
}
|
||||
|
||||
// used for type switch
|
||||
func (self *CommitFileTreeViewModel) IsFilterableContext() {}
|
||||
|
||||
func (self *CommitFileTreeViewModel) FilterPrefix(tr *i18n.TranslationSet) string {
|
||||
return tr.FilterPrefix
|
||||
}
|
||||
|
||||
func (self *CommitFileTreeViewModel) GetSearchHistory() *utils.HistoryBuffer[string] {
|
||||
return self.searchHistory
|
||||
}
|
||||
|
|
|
|||
48
pkg/gui/filetree/file_filter.go
Normal file
48
pkg/gui/filetree/file_filter.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package filetree
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sahilm/fuzzy"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type filePathSource struct {
|
||||
files []*models.File
|
||||
}
|
||||
|
||||
func (s *filePathSource) String(i int) string {
|
||||
return s.files[i].Path
|
||||
}
|
||||
|
||||
func (s *filePathSource) Len() int {
|
||||
return len(s.files)
|
||||
}
|
||||
|
||||
func filterFilesByText(files []*models.File, filter string, useFuzzySearch bool) []*models.File {
|
||||
source := &filePathSource{files: files}
|
||||
matches := utils.FindFrom(filter, source, useFuzzySearch)
|
||||
return lo.Map(matches, func(match fuzzy.Match, _ int) *models.File {
|
||||
return files[match.Index]
|
||||
})
|
||||
}
|
||||
|
||||
type commitFilePathSource struct {
|
||||
files []*models.CommitFile
|
||||
}
|
||||
|
||||
func (s *commitFilePathSource) String(i int) string {
|
||||
return s.files[i].Path
|
||||
}
|
||||
|
||||
func (s *commitFilePathSource) Len() int {
|
||||
return len(s.files)
|
||||
}
|
||||
|
||||
func filterCommitFilesByText(files []*models.CommitFile, filter string, useFuzzySearch bool) []*models.CommitFile {
|
||||
source := &commitFilePathSource{files: files}
|
||||
matches := utils.FindFrom(filter, source, useFuzzySearch)
|
||||
return lo.Map(matches, func(match fuzzy.Match, _ int) *models.CommitFile {
|
||||
return files[match.Index]
|
||||
})
|
||||
}
|
||||
|
|
@ -48,6 +48,8 @@ type IFileTree interface {
|
|||
GetAllFiles() []*models.File
|
||||
GetStatusFilter() FileTreeDisplayFilter
|
||||
GetRoot() *FileNode
|
||||
SetTextFilter(filter string, useFuzzySearch bool)
|
||||
GetTextFilter() string
|
||||
}
|
||||
|
||||
type FileTree struct {
|
||||
|
|
@ -57,6 +59,8 @@ type FileTree struct {
|
|||
common *common.Common
|
||||
filter FileTreeDisplayFilter
|
||||
collapsedPaths *CollapsedPaths
|
||||
textFilter string
|
||||
useFuzzySearch bool
|
||||
}
|
||||
|
||||
var _ IFileTree = &FileTree{}
|
||||
|
|
@ -80,24 +84,31 @@ func (self *FileTree) ExpandToPath(path string) {
|
|||
}
|
||||
|
||||
func (self *FileTree) getFilesForDisplay() []*models.File {
|
||||
var files []*models.File
|
||||
switch self.filter {
|
||||
case DisplayAll:
|
||||
return self.getFiles()
|
||||
files = self.getFiles()
|
||||
case DisplayStaged:
|
||||
return self.FilterFiles(func(file *models.File) bool { return file.HasStagedChanges })
|
||||
files = self.FilterFiles(func(file *models.File) bool { return file.HasStagedChanges })
|
||||
case DisplayUnstaged:
|
||||
return self.FilterFiles(func(file *models.File) bool { return file.HasUnstagedChanges })
|
||||
files = self.FilterFiles(func(file *models.File) bool { return file.HasUnstagedChanges })
|
||||
case DisplayTracked:
|
||||
// untracked but staged files are technically not tracked by git
|
||||
// but including such files in the filtered mode helps see what files are getting committed
|
||||
return self.FilterFiles(func(file *models.File) bool { return file.Tracked || file.HasStagedChanges })
|
||||
files = self.FilterFiles(func(file *models.File) bool { return file.Tracked || file.HasStagedChanges })
|
||||
case DisplayUntracked:
|
||||
return self.FilterFiles(func(file *models.File) bool { return !(file.Tracked || file.HasStagedChanges) })
|
||||
files = self.FilterFiles(func(file *models.File) bool { return !(file.Tracked || file.HasStagedChanges) })
|
||||
case DisplayConflicted:
|
||||
return self.FilterFiles(func(file *models.File) bool { return file.HasMergeConflicts })
|
||||
files = self.FilterFiles(func(file *models.File) bool { return file.HasMergeConflicts })
|
||||
default:
|
||||
panic(fmt.Sprintf("Unexpected files display filter: %d", self.filter))
|
||||
}
|
||||
|
||||
if self.textFilter != "" {
|
||||
files = filterFilesByText(files, self.textFilter, self.useFuzzySearch)
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
func (self *FileTree) ForceShowUntracked() bool {
|
||||
|
|
@ -213,3 +224,13 @@ func (self *FileTree) CollapsedPaths() *CollapsedPaths {
|
|||
func (self *FileTree) GetStatusFilter() FileTreeDisplayFilter {
|
||||
return self.filter
|
||||
}
|
||||
|
||||
func (self *FileTree) SetTextFilter(filter string, useFuzzySearch bool) {
|
||||
self.textFilter = filter
|
||||
self.useFuzzySearch = useFuzzySearch
|
||||
self.SetTree()
|
||||
}
|
||||
|
||||
func (self *FileTree) GetTextFilter() string {
|
||||
return self.textFilter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context/traits"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
|
@ -24,6 +25,7 @@ type FileTreeViewModel struct {
|
|||
sync.RWMutex
|
||||
types.IListCursor
|
||||
IFileTree
|
||||
searchHistory *utils.HistoryBuffer[string]
|
||||
}
|
||||
|
||||
var _ IFileTreeViewModel = &FileTreeViewModel{}
|
||||
|
|
@ -32,8 +34,9 @@ func NewFileTreeViewModel(getFiles func() []*models.File, common *common.Common,
|
|||
fileTree := NewFileTree(getFiles, common, showTree)
|
||||
listCursor := traits.NewListCursor(fileTree.Len)
|
||||
return &FileTreeViewModel{
|
||||
IFileTree: fileTree,
|
||||
IListCursor: listCursor,
|
||||
IFileTree: fileTree,
|
||||
IListCursor: listCursor,
|
||||
searchHistory: utils.NewHistoryBuffer[string](1000),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -220,3 +223,51 @@ func (self *FileTreeViewModel) ExpandAll() {
|
|||
self.SetSelectedLineIdx(index)
|
||||
}
|
||||
}
|
||||
|
||||
// IFilterableContext methods
|
||||
|
||||
func (self *FileTreeViewModel) SetFilter(filter string, useFuzzySearch bool) {
|
||||
self.IFileTree.SetTextFilter(filter, useFuzzySearch)
|
||||
}
|
||||
|
||||
func (self *FileTreeViewModel) GetFilter() string {
|
||||
return self.IFileTree.GetTextFilter()
|
||||
}
|
||||
|
||||
func (self *FileTreeViewModel) ClearFilter() {
|
||||
selectedNode := self.GetSelected()
|
||||
var selectedPath string
|
||||
if selectedNode != nil {
|
||||
selectedPath = selectedNode.GetInternalPath()
|
||||
}
|
||||
|
||||
self.IFileTree.SetTextFilter("", false)
|
||||
|
||||
if selectedPath != "" {
|
||||
self.ExpandToPath(selectedPath)
|
||||
if idx, found := self.GetIndexForPath(selectedPath); found {
|
||||
self.SetSelection(idx)
|
||||
return
|
||||
}
|
||||
}
|
||||
self.ClampSelection()
|
||||
}
|
||||
|
||||
func (self *FileTreeViewModel) ReApplyFilter(useFuzzySearch bool) {
|
||||
self.IFileTree.SetTextFilter(self.IFileTree.GetTextFilter(), useFuzzySearch)
|
||||
}
|
||||
|
||||
func (self *FileTreeViewModel) IsFiltering() bool {
|
||||
return self.IFileTree.GetTextFilter() != ""
|
||||
}
|
||||
|
||||
// used for type switch
|
||||
func (self *FileTreeViewModel) IsFilterableContext() {}
|
||||
|
||||
func (self *FileTreeViewModel) FilterPrefix(tr *i18n.TranslationSet) string {
|
||||
return tr.FilterPrefix
|
||||
}
|
||||
|
||||
func (self *FileTreeViewModel) GetSearchHistory() *utils.HistoryBuffer[string] {
|
||||
return self.searchHistory
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
var FilterCommitFiles = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Basic commit file filtering by text",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: true, // skipping until we have implemented file view filtering
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateDir("folder1")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
var FilterFiles = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Basic file filtering by text",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: true, // Skipping until we have implemented file view filtering
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateDir("folder1")
|
||||
|
|
|
|||
|
|
@ -70,10 +70,7 @@ var NestedFilter = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
).
|
||||
FilterOrSearch("grape").
|
||||
Lines(
|
||||
Equals("▼ /"),
|
||||
Equals(" A apple"),
|
||||
Equals(" A grape").IsSelected(),
|
||||
Equals(" A orange"),
|
||||
Contains("grape").IsSelected(),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
|
|
@ -91,15 +88,12 @@ var NestedFilter = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ /"),
|
||||
Equals(" A apple"),
|
||||
Equals(" A grape").IsSelected(),
|
||||
Equals(" A orange"),
|
||||
Contains("grape").IsSelected(),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Views().Search().IsVisible().Content(Contains("matches for 'grape'"))
|
||||
}).
|
||||
// cancel search
|
||||
// cancel filter
|
||||
PressEscape().
|
||||
Tap(func() {
|
||||
t.Views().Search().IsInvisible()
|
||||
|
|
|
|||
|
|
@ -75,9 +75,7 @@ var NestedFilterTransient = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
).
|
||||
FilterOrSearch("two").
|
||||
Lines(
|
||||
Equals("▼ /"),
|
||||
Equals(" A file-one"),
|
||||
Equals(" A file-two").IsSelected(),
|
||||
Contains("file-two").IsSelected(),
|
||||
)
|
||||
|
||||
t.Views().Branches().
|
||||
|
|
@ -96,7 +94,7 @@ var NestedFilterTransient = NewIntegrationTest(NewIntegrationTestArgs{
|
|||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
// the search on the commit-files context has been cancelled
|
||||
// the filter on the commit-files context has been cancelled
|
||||
Lines(
|
||||
Equals("▼ /").IsSelected(),
|
||||
Equals(" A file-one"),
|
||||
|
|
|
|||
Loading…
Reference in a new issue