mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-04-24 20:56:17 +02:00
615c566ac4
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, only the visible filtered files in the directory are affected, consistent with how staging a directory in the files panel works. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
259 lines
6.5 KiB
Go
259 lines
6.5 KiB
Go
package filetree
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
|
"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"
|
|
)
|
|
|
|
type ICommitFileTreeViewModel interface {
|
|
ICommitFileTree
|
|
types.IListCursor
|
|
|
|
GetRef() models.Ref
|
|
SetRef(models.Ref)
|
|
GetRefRange() *types.RefRange // can be nil, in which case GetRef should be used
|
|
SetRefRange(*types.RefRange) // should be set to nil when selection is not a range
|
|
GetCanRebase() bool
|
|
SetCanRebase(bool)
|
|
}
|
|
|
|
type CommitFileTreeViewModel struct {
|
|
sync.RWMutex
|
|
types.IListCursor
|
|
ICommitFileTree
|
|
|
|
// this is e.g. the commit for which we're viewing the files, if there is no
|
|
// range selection, or if the range selection can't be used for some reason
|
|
ref models.Ref
|
|
|
|
// this is a commit range for which we're viewing the files. Can be nil, in
|
|
// which case ref is used.
|
|
refRange *types.RefRange
|
|
|
|
// 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{}
|
|
|
|
func NewCommitFileTreeViewModel(getFiles func() []*models.CommitFile, common *common.Common, showTree bool) *CommitFileTreeViewModel {
|
|
fileTree := NewCommitFileTree(getFiles, common, showTree)
|
|
listCursor := traits.NewListCursor(fileTree.Len)
|
|
return &CommitFileTreeViewModel{
|
|
ICommitFileTree: fileTree,
|
|
IListCursor: listCursor,
|
|
ref: nil,
|
|
refRange: nil,
|
|
canRebase: false,
|
|
searchHistory: utils.NewHistoryBuffer[string](1000),
|
|
}
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetRef() models.Ref {
|
|
return self.ref
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) SetRef(ref models.Ref) {
|
|
self.ref = ref
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetRefRange() *types.RefRange {
|
|
return self.refRange
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) SetRefRange(refsForRange *types.RefRange) {
|
|
self.refRange = refsForRange
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetCanRebase() bool {
|
|
return self.canRebase
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) SetCanRebase(canRebase bool) {
|
|
self.canRebase = canRebase
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetSelected() *CommitFileNode {
|
|
if self.Len() == 0 {
|
|
return nil
|
|
}
|
|
|
|
return self.Get(self.GetSelectedLineIdx())
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetSelectedItemId() string {
|
|
item := self.GetSelected()
|
|
if item == nil {
|
|
return ""
|
|
}
|
|
|
|
return item.ID()
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetSelectedItems() ([]*CommitFileNode, int, int) {
|
|
if self.Len() == 0 {
|
|
return nil, 0, 0
|
|
}
|
|
|
|
startIdx, endIdx := self.GetSelectionRange()
|
|
|
|
nodes := []*CommitFileNode{}
|
|
for i := startIdx; i <= endIdx; i++ {
|
|
nodes = append(nodes, self.Get(i))
|
|
}
|
|
|
|
return nodes, startIdx, endIdx
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetSelectedItemIds() ([]string, int, int) {
|
|
selectedItems, startIdx, endIdx := self.GetSelectedItems()
|
|
|
|
ids := lo.Map(selectedItems, func(item *CommitFileNode, _ int) string {
|
|
return item.ID()
|
|
})
|
|
|
|
return ids, startIdx, endIdx
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetSelectedFile() *models.CommitFile {
|
|
node := self.GetSelected()
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
|
|
return node.File
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) GetSelectedPath() string {
|
|
node := self.GetSelected()
|
|
if node == nil {
|
|
return ""
|
|
}
|
|
|
|
return node.GetPath()
|
|
}
|
|
|
|
// duplicated from file_tree_view_model.go. Generics will help here
|
|
func (self *CommitFileTreeViewModel) ToggleShowTree() {
|
|
selectedNode := self.GetSelected()
|
|
|
|
self.ICommitFileTree.ToggleShowTree()
|
|
|
|
if selectedNode == nil {
|
|
return
|
|
}
|
|
path := selectedNode.path
|
|
|
|
if self.InTreeMode() {
|
|
self.ExpandToPath(path)
|
|
} else if len(selectedNode.Children) > 0 {
|
|
path = selectedNode.GetLeaves()[0].path
|
|
}
|
|
|
|
index, found := self.GetIndexForPath(path)
|
|
if found {
|
|
self.SetSelection(index)
|
|
}
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) CollapseAll() {
|
|
selectedNode := self.GetSelected()
|
|
|
|
self.ICommitFileTree.CollapseAll()
|
|
if selectedNode == nil {
|
|
return
|
|
}
|
|
|
|
topLevelPath := strings.Split(selectedNode.path, "/")[0]
|
|
index, found := self.GetIndexForPath(topLevelPath)
|
|
if found {
|
|
self.SetSelectedLineIdx(index)
|
|
}
|
|
}
|
|
|
|
func (self *CommitFileTreeViewModel) ExpandAll() {
|
|
selectedNode := self.GetSelected()
|
|
|
|
self.ICommitFileTree.ExpandAll()
|
|
|
|
if selectedNode == nil {
|
|
return
|
|
}
|
|
|
|
index, found := self.GetIndexForPath(selectedNode.path)
|
|
if found {
|
|
self.SetSelectedLineIdx(index)
|
|
}
|
|
}
|
|
|
|
// Try to select the given path if present. If it doesn't exist, or one of the parent directories is
|
|
// collapsed, do nothing.
|
|
// Note that filepath is an actual file path, not an internal tree path as with e.g.
|
|
// ToggleCollapsed. It must be a relative path (relative to the repo root), and it must contain
|
|
// forward slashes rather than backslashes even on Windows.
|
|
func (self *CommitFileTreeViewModel) SelectPath(filepath string, showRootItem bool) {
|
|
index, found := self.GetIndexForPath(InternalTreePathForFilePath(filepath, showRootItem))
|
|
if found {
|
|
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
|
|
}
|