mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-04 23:37:41 +02:00
Add user config gui.addRootItemInFileTree
This commit is contained in:
parent
ffb8586795
commit
3cff48437e
@ -190,6 +190,9 @@ gui:
|
||||
# This can be toggled from within Lazygit with the '`' key, but that will not change the default.
|
||||
showFileTree: true
|
||||
|
||||
# If true, add a "/" root item in the file tree representing the root of the repository. It is only added when necessary, i.e. when there is more than one item at top level.
|
||||
showRootItemInFileTree: true
|
||||
|
||||
# If true, show the number of lines changed per file in the Files view
|
||||
showNumstatInFilesView: false
|
||||
|
||||
|
@ -123,6 +123,8 @@ type GuiConfig struct {
|
||||
// If true, display the files in the file views as a tree. If false, display the files as a flat list.
|
||||
// This can be toggled from within Lazygit with the '`' key, but that will not change the default.
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
// If true, add a "/" root item in the file tree representing the root of the repository. It is only added when necessary, i.e. when there is more than one item at top level.
|
||||
ShowRootItemInFileTree bool `yaml:"showRootItemInFileTree"`
|
||||
// If true, show the number of lines changed per file in the Files view
|
||||
ShowNumstatInFilesView bool `yaml:"showNumstatInFilesView"`
|
||||
// If true, show a random tip in the command log when Lazygit starts
|
||||
@ -764,6 +766,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
ShowBottomLine: true,
|
||||
ShowPanelJumps: true,
|
||||
ShowFileTree: true,
|
||||
ShowRootItemInFileTree: true,
|
||||
ShowNumstatInFilesView: false,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
|
@ -31,7 +31,7 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
||||
showNumstat := c.UserConfig().Gui.ShowNumstatInFilesView
|
||||
lines := presentation.RenderFileTree(viewModel, c.Model().Submodules, showFileIcons, showNumstat, &c.UserConfig().Gui.CustomIcons)
|
||||
lines := presentation.RenderFileTree(viewModel, c.Model().Submodules, showFileIcons, showNumstat, &c.UserConfig().Gui.CustomIcons, c.UserConfig().Gui.ShowRootItemInFileTree)
|
||||
return lo.Map(lines, func(line string, _ int) []string {
|
||||
return []string{line}
|
||||
})
|
||||
|
@ -7,14 +7,14 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
)
|
||||
|
||||
func BuildTreeFromFiles(files []*models.File) *Node[models.File] {
|
||||
func BuildTreeFromFiles(files []*models.File, showRootItem bool) *Node[models.File] {
|
||||
root := &Node[models.File]{}
|
||||
|
||||
childrenMapsByNode := make(map[*Node[models.File]]map[string]*Node[models.File])
|
||||
|
||||
var curr *Node[models.File]
|
||||
for _, file := range files {
|
||||
splitPath := split("./" + file.Path)
|
||||
splitPath := SplitFileTreePath(file.Path, showRootItem)
|
||||
curr = root
|
||||
outer:
|
||||
for i := range splitPath {
|
||||
@ -63,19 +63,19 @@ func BuildTreeFromFiles(files []*models.File) *Node[models.File] {
|
||||
return root
|
||||
}
|
||||
|
||||
func BuildFlatTreeFromCommitFiles(files []*models.CommitFile) *Node[models.CommitFile] {
|
||||
rootAux := BuildTreeFromCommitFiles(files)
|
||||
func BuildFlatTreeFromCommitFiles(files []*models.CommitFile, showRootItem bool) *Node[models.CommitFile] {
|
||||
rootAux := BuildTreeFromCommitFiles(files, showRootItem)
|
||||
sortedFiles := rootAux.GetLeaves()
|
||||
|
||||
return &Node[models.CommitFile]{Children: sortedFiles}
|
||||
}
|
||||
|
||||
func BuildTreeFromCommitFiles(files []*models.CommitFile) *Node[models.CommitFile] {
|
||||
func BuildTreeFromCommitFiles(files []*models.CommitFile, showRootItem bool) *Node[models.CommitFile] {
|
||||
root := &Node[models.CommitFile]{}
|
||||
|
||||
var curr *Node[models.CommitFile]
|
||||
for _, file := range files {
|
||||
splitPath := split("./" + file.Path)
|
||||
splitPath := SplitFileTreePath(file.Path, showRootItem)
|
||||
curr = root
|
||||
outer:
|
||||
for i := range splitPath {
|
||||
@ -115,8 +115,8 @@ func BuildTreeFromCommitFiles(files []*models.CommitFile) *Node[models.CommitFil
|
||||
return root
|
||||
}
|
||||
|
||||
func BuildFlatTreeFromFiles(files []*models.File) *Node[models.File] {
|
||||
rootAux := BuildTreeFromFiles(files)
|
||||
func BuildFlatTreeFromFiles(files []*models.File, showRootItem bool) *Node[models.File] {
|
||||
rootAux := BuildTreeFromFiles(files, showRootItem)
|
||||
sortedFiles := rootAux.GetLeaves()
|
||||
|
||||
// from top down we have merge conflict files, then tracked file, then untracked
|
||||
@ -160,3 +160,11 @@ func split(str string) []string {
|
||||
func join(strs []string) string {
|
||||
return strings.Join(strs, "/")
|
||||
}
|
||||
|
||||
func SplitFileTreePath(path string, showRootItem bool) []string {
|
||||
if showRootItem {
|
||||
return split("./" + path)
|
||||
}
|
||||
|
||||
return split(path)
|
||||
}
|
||||
|
@ -9,9 +9,10 @@ import (
|
||||
|
||||
func TestBuildTreeFromFiles(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
files []*models.File
|
||||
expected *Node[models.File]
|
||||
name string
|
||||
files []*models.File
|
||||
showRootItem bool
|
||||
expected *Node[models.File]
|
||||
}{
|
||||
{
|
||||
name: "no files",
|
||||
@ -31,6 +32,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -51,6 +53,37 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "files in same directory, not root item",
|
||||
files: []*models.File{
|
||||
{
|
||||
Path: "dir1/a",
|
||||
},
|
||||
{
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
{
|
||||
path: "dir1",
|
||||
CompressionLevel: 0,
|
||||
Children: []*Node[models.File]{
|
||||
{
|
||||
File: &models.File{Path: "dir1/a"},
|
||||
path: "dir1/a",
|
||||
},
|
||||
{
|
||||
File: &models.File{Path: "dir1/b"},
|
||||
path: "dir1/b",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be compressed",
|
||||
files: []*models.File{
|
||||
@ -61,6 +94,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
Path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -92,6 +126,43 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be compressed, no root item",
|
||||
files: []*models.File{
|
||||
{
|
||||
Path: "dir1/dir3/a",
|
||||
},
|
||||
{
|
||||
Path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
{
|
||||
path: "dir1/dir3",
|
||||
Children: []*Node[models.File]{
|
||||
{
|
||||
File: &models.File{Path: "dir1/dir3/a"},
|
||||
path: "dir1/dir3/a",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
{
|
||||
path: "dir2/dir4",
|
||||
Children: []*Node[models.File]{
|
||||
{
|
||||
File: &models.File{Path: "dir2/dir4/b"},
|
||||
path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be sorted",
|
||||
files: []*models.File{
|
||||
@ -102,6 +173,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
Path: "a",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -135,6 +207,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
Path: "a",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -164,7 +237,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
result := BuildTreeFromFiles(s.files)
|
||||
result := BuildTreeFromFiles(s.files, s.showRootItem)
|
||||
assert.EqualValues(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
@ -172,9 +245,10 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
|
||||
func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
files []*models.File
|
||||
expected *Node[models.File]
|
||||
name string
|
||||
files []*models.File
|
||||
showRootItem bool
|
||||
expected *Node[models.File]
|
||||
}{
|
||||
{
|
||||
name: "no files",
|
||||
@ -194,6 +268,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -210,6 +285,33 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "files in same directory, not root item",
|
||||
files: []*models.File{
|
||||
{
|
||||
Path: "dir1/a",
|
||||
},
|
||||
{
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
{
|
||||
File: &models.File{Path: "dir1/a"},
|
||||
path: "dir1/a",
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
{
|
||||
File: &models.File{Path: "dir1/b"},
|
||||
path: "dir1/b",
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be compressed",
|
||||
files: []*models.File{
|
||||
@ -220,6 +322,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
Path: "dir2/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -236,6 +339,33 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be compressed, no root item",
|
||||
files: []*models.File{
|
||||
{
|
||||
Path: "dir1/a",
|
||||
},
|
||||
{
|
||||
Path: "dir2/b",
|
||||
},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
{
|
||||
File: &models.File{Path: "dir1/a"},
|
||||
path: "dir1/a",
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
{
|
||||
File: &models.File{Path: "dir2/b"},
|
||||
path: "dir2/b",
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be sorted",
|
||||
files: []*models.File{
|
||||
@ -246,6 +376,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
Path: "a",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -288,6 +419,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
Tracked: true,
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.File]{
|
||||
path: "",
|
||||
Children: []*Node[models.File]{
|
||||
@ -322,7 +454,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
result := BuildFlatTreeFromFiles(s.files)
|
||||
result := BuildFlatTreeFromFiles(s.files, s.showRootItem)
|
||||
assert.EqualValues(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
@ -330,9 +462,10 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
|
||||
func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
files []*models.CommitFile
|
||||
expected *Node[models.CommitFile]
|
||||
name string
|
||||
files []*models.CommitFile
|
||||
showRootItem bool
|
||||
expected *Node[models.CommitFile]
|
||||
}{
|
||||
{
|
||||
name: "no files",
|
||||
@ -352,6 +485,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
@ -372,6 +506,37 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "files in same directory, not root item",
|
||||
files: []*models.CommitFile{
|
||||
{
|
||||
Path: "dir1/a",
|
||||
},
|
||||
{
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
{
|
||||
path: "dir1",
|
||||
CompressionLevel: 0,
|
||||
Children: []*Node[models.CommitFile]{
|
||||
{
|
||||
File: &models.CommitFile{Path: "dir1/a"},
|
||||
path: "dir1/a",
|
||||
},
|
||||
{
|
||||
File: &models.CommitFile{Path: "dir1/b"},
|
||||
path: "dir1/b",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be compressed",
|
||||
files: []*models.CommitFile{
|
||||
@ -382,6 +547,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
Path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
@ -413,6 +579,43 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be compressed, no root item",
|
||||
files: []*models.CommitFile{
|
||||
{
|
||||
Path: "dir1/dir3/a",
|
||||
},
|
||||
{
|
||||
Path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
{
|
||||
path: "dir1/dir3",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
{
|
||||
File: &models.CommitFile{Path: "dir1/dir3/a"},
|
||||
path: "dir1/dir3/a",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
{
|
||||
path: "dir2/dir4",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
{
|
||||
File: &models.CommitFile{Path: "dir2/dir4/b"},
|
||||
path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be sorted",
|
||||
files: []*models.CommitFile{
|
||||
@ -423,6 +626,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
Path: "a",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
@ -446,7 +650,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
result := BuildTreeFromCommitFiles(s.files)
|
||||
result := BuildTreeFromCommitFiles(s.files, s.showRootItem)
|
||||
assert.EqualValues(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
@ -454,9 +658,10 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
|
||||
func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
files []*models.CommitFile
|
||||
expected *Node[models.CommitFile]
|
||||
name string
|
||||
files []*models.CommitFile
|
||||
showRootItem bool
|
||||
expected *Node[models.CommitFile]
|
||||
}{
|
||||
{
|
||||
name: "no files",
|
||||
@ -476,6 +681,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
@ -492,6 +698,33 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "files in same directory, not root item",
|
||||
files: []*models.CommitFile{
|
||||
{
|
||||
Path: "dir1/a",
|
||||
},
|
||||
{
|
||||
Path: "dir1/b",
|
||||
},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
{
|
||||
File: &models.CommitFile{Path: "dir1/a"},
|
||||
path: "dir1/a",
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
{
|
||||
File: &models.CommitFile{Path: "dir1/b"},
|
||||
path: "dir1/b",
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "paths that can be compressed",
|
||||
files: []*models.CommitFile{
|
||||
@ -502,6 +735,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||
Path: "dir2/b",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
@ -528,6 +762,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||
Path: "a",
|
||||
},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: &Node[models.CommitFile]{
|
||||
path: "",
|
||||
Children: []*Node[models.CommitFile]{
|
||||
@ -546,7 +781,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
result := BuildFlatTreeFromCommitFiles(s.files)
|
||||
result := BuildFlatTreeFromCommitFiles(s.files, s.showRootItem)
|
||||
assert.EqualValues(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
|
@ -94,10 +94,11 @@ func (self *CommitFileTree) GetAllFiles() []*models.CommitFile {
|
||||
}
|
||||
|
||||
func (self *CommitFileTree) SetTree() {
|
||||
showRootItem := self.common.UserConfig().Gui.ShowRootItemInFileTree
|
||||
if self.showTree {
|
||||
self.tree = BuildTreeFromCommitFiles(self.getFiles())
|
||||
self.tree = BuildTreeFromCommitFiles(self.getFiles(), showRootItem)
|
||||
} else {
|
||||
self.tree = BuildFlatTreeFromCommitFiles(self.getFiles())
|
||||
self.tree = BuildFlatTreeFromCommitFiles(self.getFiles(), showRootItem)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,10 +168,11 @@ func (self *FileTree) GetAllFiles() []*models.File {
|
||||
|
||||
func (self *FileTree) SetTree() {
|
||||
filesForDisplay := self.getFilesForDisplay()
|
||||
showRootItem := self.common.UserConfig().Gui.ShowRootItemInFileTree
|
||||
if self.showTree {
|
||||
self.tree = BuildTreeFromFiles(filesForDisplay)
|
||||
self.tree = BuildTreeFromFiles(filesForDisplay, showRootItem)
|
||||
} else {
|
||||
self.tree = BuildFlatTreeFromFiles(filesForDisplay)
|
||||
self.tree = BuildFlatTreeFromFiles(filesForDisplay, showRootItem)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,13 @@ func RenderFileTree(
|
||||
showFileIcons bool,
|
||||
showNumstat bool,
|
||||
customIconsConfig *config.CustomIconsConfig,
|
||||
showRootItem bool,
|
||||
) []string {
|
||||
collapsedPaths := tree.CollapsedPaths()
|
||||
return renderAux(tree.GetRoot().Raw(), collapsedPaths, -1, -1, func(node *filetree.Node[models.File], treeDepth int, visualDepth int, isCollapsed bool) string {
|
||||
fileNode := filetree.NewFileNode(node)
|
||||
|
||||
return getFileLine(isCollapsed, fileNode.GetHasUnstagedChanges(), fileNode.GetHasStagedChanges(), treeDepth, visualDepth, showNumstat, showFileIcons, submoduleConfigs, node, customIconsConfig)
|
||||
return getFileLine(isCollapsed, fileNode.GetHasUnstagedChanges(), fileNode.GetHasStagedChanges(), treeDepth, visualDepth, showNumstat, showFileIcons, submoduleConfigs, node, customIconsConfig, showRootItem)
|
||||
})
|
||||
}
|
||||
|
||||
@ -120,8 +121,9 @@ func getFileLine(
|
||||
submoduleConfigs []*models.SubmoduleConfig,
|
||||
node *filetree.Node[models.File],
|
||||
customIconsConfig *config.CustomIconsConfig,
|
||||
showRootItem bool,
|
||||
) string {
|
||||
name := fileNameAtDepth(node, treeDepth)
|
||||
name := fileNameAtDepth(node, treeDepth, showRootItem)
|
||||
output := ""
|
||||
|
||||
var nameColor style.TextStyle
|
||||
@ -297,7 +299,7 @@ func getColorForChangeStatus(changeStatus string) style.TextStyle {
|
||||
}
|
||||
}
|
||||
|
||||
func fileNameAtDepth(node *filetree.Node[models.File], depth int) string {
|
||||
func fileNameAtDepth(node *filetree.Node[models.File], depth int, showRootItem bool) string {
|
||||
splitName := split(node.GetInternalPath())
|
||||
if depth == 0 && splitName[0] == "." {
|
||||
if len(splitName) == 1 {
|
||||
@ -308,7 +310,7 @@ func fileNameAtDepth(node *filetree.Node[models.File], depth int) string {
|
||||
name := join(splitName[depth:])
|
||||
|
||||
if node.File != nil && node.File.IsRename() {
|
||||
splitPrevName := split("./" + node.File.PreviousPath)
|
||||
splitPrevName := filetree.SplitFileTreePath(node.File.PreviousPath, showRootItem)
|
||||
|
||||
prevName := node.File.PreviousPath
|
||||
// if the file has just been renamed inside the same directory, we can shave off
|
||||
|
@ -26,6 +26,7 @@ func TestRenderFileTree(t *testing.T) {
|
||||
files []*models.File
|
||||
collapsedPaths []string
|
||||
showLineChanges bool
|
||||
showRootItem bool
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
@ -38,7 +39,8 @@ func TestRenderFileTree(t *testing.T) {
|
||||
files: []*models.File{
|
||||
{Path: "test", ShortStatus: " M", HasStagedChanges: true},
|
||||
},
|
||||
expected: []string{" M test"},
|
||||
showRootItem: true,
|
||||
expected: []string{" M test"},
|
||||
},
|
||||
{
|
||||
name: "numstat",
|
||||
@ -49,6 +51,7 @@ func TestRenderFileTree(t *testing.T) {
|
||||
{Path: "test4", ShortStatus: " M", HasStagedChanges: true, LinesAdded: 0, LinesDeleted: 0},
|
||||
},
|
||||
showLineChanges: true,
|
||||
showRootItem: true,
|
||||
expected: []string{
|
||||
"▼ /",
|
||||
" M test +1 -1",
|
||||
@ -67,6 +70,7 @@ func TestRenderFileTree(t *testing.T) {
|
||||
{Path: "dir2/file5", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
{Path: "file1", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: toStringSlice(
|
||||
`
|
||||
▼ /
|
||||
@ -81,6 +85,30 @@ func TestRenderFileTree(t *testing.T) {
|
||||
),
|
||||
collapsedPaths: []string{"./dir1"},
|
||||
},
|
||||
{
|
||||
name: "big example without root item",
|
||||
files: []*models.File{
|
||||
{Path: "dir1/file2", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
{Path: "dir1/file3", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
{Path: "dir2/dir2/file3", ShortStatus: " M", HasStagedChanges: true},
|
||||
{Path: "dir2/dir2/file4", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
{Path: "dir2/file5", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
{Path: "file1", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: toStringSlice(
|
||||
`
|
||||
▶ dir1
|
||||
▼ dir2
|
||||
▼ dir2
|
||||
M file3
|
||||
M file4
|
||||
M file5
|
||||
M file1
|
||||
`,
|
||||
),
|
||||
collapsedPaths: []string{"dir1"},
|
||||
},
|
||||
}
|
||||
|
||||
oldColorLevel := color.ForceSetColorLevel(terminfo.ColorLevelNone)
|
||||
@ -89,12 +117,13 @@ func TestRenderFileTree(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
common := common.NewDummyCommon()
|
||||
common.UserConfig().Gui.ShowRootItemInFileTree = s.showRootItem
|
||||
viewModel := filetree.NewFileTree(func() []*models.File { return s.files }, common, true)
|
||||
viewModel.SetTree()
|
||||
for _, path := range s.collapsedPaths {
|
||||
viewModel.ToggleCollapsed(path)
|
||||
}
|
||||
result := RenderFileTree(viewModel, nil, false, s.showLineChanges, &config.CustomIconsConfig{})
|
||||
result := RenderFileTree(viewModel, nil, false, s.showLineChanges, &config.CustomIconsConfig{}, s.showRootItem)
|
||||
assert.EqualValues(t, s.expected, result)
|
||||
})
|
||||
}
|
||||
@ -106,6 +135,7 @@ func TestRenderCommitFileTree(t *testing.T) {
|
||||
root *filetree.FileNode
|
||||
files []*models.CommitFile
|
||||
collapsedPaths []string
|
||||
showRootItem bool
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
@ -118,7 +148,8 @@ func TestRenderCommitFileTree(t *testing.T) {
|
||||
files: []*models.CommitFile{
|
||||
{Path: "test", ChangeStatus: "A"},
|
||||
},
|
||||
expected: []string{"A test"},
|
||||
showRootItem: true,
|
||||
expected: []string{"A test"},
|
||||
},
|
||||
{
|
||||
name: "big example",
|
||||
@ -130,6 +161,7 @@ func TestRenderCommitFileTree(t *testing.T) {
|
||||
{Path: "dir2/file5", ChangeStatus: "M"},
|
||||
{Path: "file1", ChangeStatus: "M"},
|
||||
},
|
||||
showRootItem: true,
|
||||
expected: toStringSlice(
|
||||
`
|
||||
▼ /
|
||||
@ -144,6 +176,30 @@ func TestRenderCommitFileTree(t *testing.T) {
|
||||
),
|
||||
collapsedPaths: []string{"./dir1"},
|
||||
},
|
||||
{
|
||||
name: "big example without root item",
|
||||
files: []*models.CommitFile{
|
||||
{Path: "dir1/file2", ChangeStatus: "M"},
|
||||
{Path: "dir1/file3", ChangeStatus: "A"},
|
||||
{Path: "dir2/dir2/file3", ChangeStatus: "D"},
|
||||
{Path: "dir2/dir2/file4", ChangeStatus: "M"},
|
||||
{Path: "dir2/file5", ChangeStatus: "M"},
|
||||
{Path: "file1", ChangeStatus: "M"},
|
||||
},
|
||||
showRootItem: false,
|
||||
expected: toStringSlice(
|
||||
`
|
||||
▶ dir1
|
||||
▼ dir2
|
||||
▼ dir2
|
||||
D file3
|
||||
M file4
|
||||
M file5
|
||||
M file1
|
||||
`,
|
||||
),
|
||||
collapsedPaths: []string{"dir1"},
|
||||
},
|
||||
}
|
||||
|
||||
oldColorLevel := color.ForceSetColorLevel(terminfo.ColorLevelNone)
|
||||
@ -154,6 +210,7 @@ func TestRenderCommitFileTree(t *testing.T) {
|
||||
hashPool := &utils.StringPool{}
|
||||
|
||||
common := common.NewDummyCommon()
|
||||
common.UserConfig().Gui.ShowRootItemInFileTree = s.showRootItem
|
||||
viewModel := filetree.NewCommitFileTreeViewModel(func() []*models.CommitFile { return s.files }, common, true)
|
||||
viewModel.SetRef(models.NewCommit(hashPool, models.NewCommitOpts{Hash: "1234"}))
|
||||
viewModel.SetTree()
|
||||
|
36
pkg/integration/tests/file/renamed_files_no_root_item.go
Normal file
36
pkg/integration/tests/file/renamed_files_no_root_item.go
Normal file
@ -0,0 +1,36 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var RenamedFilesNoRootItem = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Regression test for the display of renamed files in the file tree, when the root item is disabled",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().Gui.ShowRootItemInFileTree = false
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateDir("dir")
|
||||
shell.CreateDir("dir/nested")
|
||||
shell.CreateFileAndAdd("file1", "file1 content\n")
|
||||
shell.CreateFileAndAdd("dir/file2", "file2 content\n")
|
||||
shell.CreateFileAndAdd("dir/nested/file3", "file3 content\n")
|
||||
shell.Commit("initial commit")
|
||||
shell.RunCommand([]string{"git", "mv", "file1", "dir/file1"})
|
||||
shell.RunCommand([]string{"git", "mv", "dir/file2", "dir/file2-renamed"})
|
||||
shell.RunCommand([]string{"git", "mv", "dir/nested/file3", "file3"})
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Equals("▼ dir"),
|
||||
Equals(" R file1 → file1"),
|
||||
Equals(" R file2 → file2-renamed"),
|
||||
Equals("R dir/nested/file3 → file3"),
|
||||
)
|
||||
},
|
||||
})
|
@ -207,6 +207,7 @@ var tests = []*components.IntegrationTest{
|
||||
file.RememberCommitMessageAfterFail,
|
||||
file.RenameSimilarityThresholdChange,
|
||||
file.RenamedFiles,
|
||||
file.RenamedFilesNoRootItem,
|
||||
file.StageChildrenRangeSelect,
|
||||
file.StageDeletedRangeSelect,
|
||||
file.StageRangeSelect,
|
||||
|
@ -569,6 +569,11 @@
|
||||
"description": "If true, display the files in the file views as a tree. If false, display the files as a flat list.\nThis can be toggled from within Lazygit with the '`' key, but that will not change the default.",
|
||||
"default": true
|
||||
},
|
||||
"showRootItemInFileTree": {
|
||||
"type": "boolean",
|
||||
"description": "If true, add a \"/\" root item in the file tree representing the root of the repository. It is only added when necessary, i.e. when there is more than one item at top level.",
|
||||
"default": true
|
||||
},
|
||||
"showNumstatInFilesView": {
|
||||
"type": "boolean",
|
||||
"description": "If true, show the number of lines changed per file in the Files view",
|
||||
|
Loading…
x
Reference in New Issue
Block a user