mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-08-10 22:42:00 +02:00
Add user config for hiding the root item in the file tree (#4593)
- **PR Description** In #4346 we added a `/` root item in the Files and CommitFiles panels whenever there is more than one top-level item. We made it unconditional, but I promised to add a config as soon as users ask for being able to disable it. For a while I was able to convince users who asked for it that it is useful and they don't want to turn it off, but now there's a [stronger request](https://github.com/jesseduffield/lazygit/discussions/4590#discussioncomment-13254924) from someone who refuses to upgrade to the current version, and we don't want that. So, add a config option `gui.showRootItemInFileTree` that is true by default.
This commit is contained in:
@@ -190,6 +190,9 @@ gui:
|
|||||||
# This can be toggled from within Lazygit with the '`' key, but that will not change the default.
|
# This can be toggled from within Lazygit with the '`' key, but that will not change the default.
|
||||||
showFileTree: true
|
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
|
# If true, show the number of lines changed per file in the Files view
|
||||||
showNumstatInFilesView: false
|
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.
|
// 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.
|
// This can be toggled from within Lazygit with the '`' key, but that will not change the default.
|
||||||
ShowFileTree bool `yaml:"showFileTree"`
|
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
|
// If true, show the number of lines changed per file in the Files view
|
||||||
ShowNumstatInFilesView bool `yaml:"showNumstatInFilesView"`
|
ShowNumstatInFilesView bool `yaml:"showNumstatInFilesView"`
|
||||||
// If true, show a random tip in the command log when Lazygit starts
|
// If true, show a random tip in the command log when Lazygit starts
|
||||||
@@ -764,6 +766,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
ShowBottomLine: true,
|
ShowBottomLine: true,
|
||||||
ShowPanelJumps: true,
|
ShowPanelJumps: true,
|
||||||
ShowFileTree: true,
|
ShowFileTree: true,
|
||||||
|
ShowRootItemInFileTree: true,
|
||||||
ShowNumstatInFilesView: false,
|
ShowNumstatInFilesView: false,
|
||||||
ShowRandomTip: true,
|
ShowRandomTip: true,
|
||||||
ShowIcons: false,
|
ShowIcons: false,
|
||||||
|
@@ -29,7 +29,7 @@ var (
|
|||||||
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||||
viewModel := filetree.NewCommitFileTreeViewModel(
|
viewModel := filetree.NewCommitFileTreeViewModel(
|
||||||
func() []*models.CommitFile { return c.Model().CommitFiles },
|
func() []*models.CommitFile { return c.Model().CommitFiles },
|
||||||
c.Log,
|
c.Common,
|
||||||
c.UserConfig().Gui.ShowFileTree,
|
c.UserConfig().Gui.ShowFileTree,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -24,14 +24,14 @@ var (
|
|||||||
func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||||
viewModel := filetree.NewFileTreeViewModel(
|
viewModel := filetree.NewFileTreeViewModel(
|
||||||
func() []*models.File { return c.Model().Files },
|
func() []*models.File { return c.Model().Files },
|
||||||
c.Log,
|
c.Common,
|
||||||
c.UserConfig().Gui.ShowFileTree,
|
c.UserConfig().Gui.ShowFileTree,
|
||||||
)
|
)
|
||||||
|
|
||||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||||
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
||||||
showNumstat := c.UserConfig().Gui.ShowNumstatInFilesView
|
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 lo.Map(lines, func(line string, _ int) []string {
|
||||||
return []string{line}
|
return []string{line}
|
||||||
})
|
})
|
||||||
|
@@ -7,14 +7,14 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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]{}
|
root := &Node[models.File]{}
|
||||||
|
|
||||||
childrenMapsByNode := make(map[*Node[models.File]]map[string]*Node[models.File])
|
childrenMapsByNode := make(map[*Node[models.File]]map[string]*Node[models.File])
|
||||||
|
|
||||||
var curr *Node[models.File]
|
var curr *Node[models.File]
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
splitPath := split("./" + file.Path)
|
splitPath := SplitFileTreePath(file.Path, showRootItem)
|
||||||
curr = root
|
curr = root
|
||||||
outer:
|
outer:
|
||||||
for i := range splitPath {
|
for i := range splitPath {
|
||||||
@@ -63,19 +63,19 @@ func BuildTreeFromFiles(files []*models.File) *Node[models.File] {
|
|||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildFlatTreeFromCommitFiles(files []*models.CommitFile) *Node[models.CommitFile] {
|
func BuildFlatTreeFromCommitFiles(files []*models.CommitFile, showRootItem bool) *Node[models.CommitFile] {
|
||||||
rootAux := BuildTreeFromCommitFiles(files)
|
rootAux := BuildTreeFromCommitFiles(files, showRootItem)
|
||||||
sortedFiles := rootAux.GetLeaves()
|
sortedFiles := rootAux.GetLeaves()
|
||||||
|
|
||||||
return &Node[models.CommitFile]{Children: sortedFiles}
|
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]{}
|
root := &Node[models.CommitFile]{}
|
||||||
|
|
||||||
var curr *Node[models.CommitFile]
|
var curr *Node[models.CommitFile]
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
splitPath := split("./" + file.Path)
|
splitPath := SplitFileTreePath(file.Path, showRootItem)
|
||||||
curr = root
|
curr = root
|
||||||
outer:
|
outer:
|
||||||
for i := range splitPath {
|
for i := range splitPath {
|
||||||
@@ -115,8 +115,8 @@ func BuildTreeFromCommitFiles(files []*models.CommitFile) *Node[models.CommitFil
|
|||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildFlatTreeFromFiles(files []*models.File) *Node[models.File] {
|
func BuildFlatTreeFromFiles(files []*models.File, showRootItem bool) *Node[models.File] {
|
||||||
rootAux := BuildTreeFromFiles(files)
|
rootAux := BuildTreeFromFiles(files, showRootItem)
|
||||||
sortedFiles := rootAux.GetLeaves()
|
sortedFiles := rootAux.GetLeaves()
|
||||||
|
|
||||||
// from top down we have merge conflict files, then tracked file, then untracked
|
// 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 {
|
func join(strs []string) string {
|
||||||
return strings.Join(strs, "/")
|
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) {
|
func TestBuildTreeFromFiles(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
files []*models.File
|
files []*models.File
|
||||||
expected *Node[models.File]
|
showRootItem bool
|
||||||
|
expected *Node[models.File]
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no files",
|
name: "no files",
|
||||||
@@ -31,6 +32,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
|||||||
Path: "dir1/b",
|
Path: "dir1/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
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",
|
name: "paths that can be compressed",
|
||||||
files: []*models.File{
|
files: []*models.File{
|
||||||
@@ -61,6 +94,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
|||||||
Path: "dir2/dir4/b",
|
Path: "dir2/dir4/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
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",
|
name: "paths that can be sorted",
|
||||||
files: []*models.File{
|
files: []*models.File{
|
||||||
@@ -102,6 +173,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
|||||||
Path: "a",
|
Path: "a",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
Children: []*Node[models.File]{
|
||||||
@@ -135,6 +207,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
|||||||
Path: "a",
|
Path: "a",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
Children: []*Node[models.File]{
|
||||||
@@ -164,7 +237,7 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
result := BuildTreeFromFiles(s.files)
|
result := BuildTreeFromFiles(s.files, s.showRootItem)
|
||||||
assert.EqualValues(t, s.expected, result)
|
assert.EqualValues(t, s.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -172,9 +245,10 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuildFlatTreeFromFiles(t *testing.T) {
|
func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
files []*models.File
|
files []*models.File
|
||||||
expected *Node[models.File]
|
showRootItem bool
|
||||||
|
expected *Node[models.File]
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no files",
|
name: "no files",
|
||||||
@@ -194,6 +268,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
|||||||
Path: "dir1/b",
|
Path: "dir1/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
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",
|
name: "paths that can be compressed",
|
||||||
files: []*models.File{
|
files: []*models.File{
|
||||||
@@ -220,6 +322,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
|||||||
Path: "dir2/b",
|
Path: "dir2/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
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",
|
name: "paths that can be sorted",
|
||||||
files: []*models.File{
|
files: []*models.File{
|
||||||
@@ -246,6 +376,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
|||||||
Path: "a",
|
Path: "a",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
Children: []*Node[models.File]{
|
||||||
@@ -288,6 +419,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
|||||||
Tracked: true,
|
Tracked: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.File]{
|
expected: &Node[models.File]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.File]{
|
Children: []*Node[models.File]{
|
||||||
@@ -322,7 +454,7 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
result := BuildFlatTreeFromFiles(s.files)
|
result := BuildFlatTreeFromFiles(s.files, s.showRootItem)
|
||||||
assert.EqualValues(t, s.expected, result)
|
assert.EqualValues(t, s.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -330,9 +462,10 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuildTreeFromCommitFiles(t *testing.T) {
|
func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
files []*models.CommitFile
|
files []*models.CommitFile
|
||||||
expected *Node[models.CommitFile]
|
showRootItem bool
|
||||||
|
expected *Node[models.CommitFile]
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no files",
|
name: "no files",
|
||||||
@@ -352,6 +485,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
|||||||
Path: "dir1/b",
|
Path: "dir1/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.CommitFile]{
|
expected: &Node[models.CommitFile]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.CommitFile]{
|
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",
|
name: "paths that can be compressed",
|
||||||
files: []*models.CommitFile{
|
files: []*models.CommitFile{
|
||||||
@@ -382,6 +547,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
|||||||
Path: "dir2/dir4/b",
|
Path: "dir2/dir4/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.CommitFile]{
|
expected: &Node[models.CommitFile]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.CommitFile]{
|
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",
|
name: "paths that can be sorted",
|
||||||
files: []*models.CommitFile{
|
files: []*models.CommitFile{
|
||||||
@@ -423,6 +626,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
|||||||
Path: "a",
|
Path: "a",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.CommitFile]{
|
expected: &Node[models.CommitFile]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.CommitFile]{
|
Children: []*Node[models.CommitFile]{
|
||||||
@@ -446,7 +650,7 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
result := BuildTreeFromCommitFiles(s.files)
|
result := BuildTreeFromCommitFiles(s.files, s.showRootItem)
|
||||||
assert.EqualValues(t, s.expected, result)
|
assert.EqualValues(t, s.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -454,9 +658,10 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
|||||||
|
|
||||||
func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
files []*models.CommitFile
|
files []*models.CommitFile
|
||||||
expected *Node[models.CommitFile]
|
showRootItem bool
|
||||||
|
expected *Node[models.CommitFile]
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no files",
|
name: "no files",
|
||||||
@@ -476,6 +681,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
|||||||
Path: "dir1/b",
|
Path: "dir1/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.CommitFile]{
|
expected: &Node[models.CommitFile]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.CommitFile]{
|
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",
|
name: "paths that can be compressed",
|
||||||
files: []*models.CommitFile{
|
files: []*models.CommitFile{
|
||||||
@@ -502,6 +735,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
|||||||
Path: "dir2/b",
|
Path: "dir2/b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.CommitFile]{
|
expected: &Node[models.CommitFile]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.CommitFile]{
|
Children: []*Node[models.CommitFile]{
|
||||||
@@ -528,6 +762,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
|||||||
Path: "a",
|
Path: "a",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: &Node[models.CommitFile]{
|
expected: &Node[models.CommitFile]{
|
||||||
path: "",
|
path: "",
|
||||||
Children: []*Node[models.CommitFile]{
|
Children: []*Node[models.CommitFile]{
|
||||||
@@ -546,7 +781,7 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
result := BuildFlatTreeFromCommitFiles(s.files)
|
result := BuildFlatTreeFromCommitFiles(s.files, s.showRootItem)
|
||||||
assert.EqualValues(t, s.expected, result)
|
assert.EqualValues(t, s.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,9 @@ package filetree
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ICommitFileTree interface {
|
type ICommitFileTree interface {
|
||||||
@@ -21,7 +21,7 @@ type CommitFileTree struct {
|
|||||||
getFiles func() []*models.CommitFile
|
getFiles func() []*models.CommitFile
|
||||||
tree *Node[models.CommitFile]
|
tree *Node[models.CommitFile]
|
||||||
showTree bool
|
showTree bool
|
||||||
log *logrus.Entry
|
common *common.Common
|
||||||
collapsedPaths *CollapsedPaths
|
collapsedPaths *CollapsedPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,10 +41,10 @@ func (self *CommitFileTree) ExpandAll() {
|
|||||||
|
|
||||||
var _ ICommitFileTree = &CommitFileTree{}
|
var _ ICommitFileTree = &CommitFileTree{}
|
||||||
|
|
||||||
func NewCommitFileTree(getFiles func() []*models.CommitFile, log *logrus.Entry, showTree bool) *CommitFileTree {
|
func NewCommitFileTree(getFiles func() []*models.CommitFile, common *common.Common, showTree bool) *CommitFileTree {
|
||||||
return &CommitFileTree{
|
return &CommitFileTree{
|
||||||
getFiles: getFiles,
|
getFiles: getFiles,
|
||||||
log: log,
|
common: common,
|
||||||
showTree: showTree,
|
showTree: showTree,
|
||||||
collapsedPaths: NewCollapsedPaths(),
|
collapsedPaths: NewCollapsedPaths(),
|
||||||
}
|
}
|
||||||
@@ -94,10 +94,11 @@ func (self *CommitFileTree) GetAllFiles() []*models.CommitFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitFileTree) SetTree() {
|
func (self *CommitFileTree) SetTree() {
|
||||||
|
showRootItem := self.common.UserConfig().Gui.ShowRootItemInFileTree
|
||||||
if self.showTree {
|
if self.showTree {
|
||||||
self.tree = BuildTreeFromCommitFiles(self.getFiles())
|
self.tree = BuildTreeFromCommitFiles(self.getFiles(), showRootItem)
|
||||||
} else {
|
} else {
|
||||||
self.tree = BuildFlatTreeFromCommitFiles(self.getFiles())
|
self.tree = BuildFlatTreeFromCommitFiles(self.getFiles(), showRootItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,10 +5,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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/context/traits"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ICommitFileTreeViewModel interface {
|
type ICommitFileTreeViewModel interface {
|
||||||
@@ -43,8 +43,8 @@ type CommitFileTreeViewModel struct {
|
|||||||
|
|
||||||
var _ ICommitFileTreeViewModel = &CommitFileTreeViewModel{}
|
var _ ICommitFileTreeViewModel = &CommitFileTreeViewModel{}
|
||||||
|
|
||||||
func NewCommitFileTreeViewModel(getFiles func() []*models.CommitFile, log *logrus.Entry, showTree bool) *CommitFileTreeViewModel {
|
func NewCommitFileTreeViewModel(getFiles func() []*models.CommitFile, common *common.Common, showTree bool) *CommitFileTreeViewModel {
|
||||||
fileTree := NewCommitFileTree(getFiles, log, showTree)
|
fileTree := NewCommitFileTree(getFiles, common, showTree)
|
||||||
listCursor := traits.NewListCursor(fileTree.Len)
|
listCursor := traits.NewListCursor(fileTree.Len)
|
||||||
return &CommitFileTreeViewModel{
|
return &CommitFileTreeViewModel{
|
||||||
ICommitFileTree: fileTree,
|
ICommitFileTree: fileTree,
|
||||||
|
@@ -4,9 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileTreeDisplayFilter int
|
type FileTreeDisplayFilter int
|
||||||
@@ -54,17 +54,17 @@ type FileTree struct {
|
|||||||
getFiles func() []*models.File
|
getFiles func() []*models.File
|
||||||
tree *Node[models.File]
|
tree *Node[models.File]
|
||||||
showTree bool
|
showTree bool
|
||||||
log *logrus.Entry
|
common *common.Common
|
||||||
filter FileTreeDisplayFilter
|
filter FileTreeDisplayFilter
|
||||||
collapsedPaths *CollapsedPaths
|
collapsedPaths *CollapsedPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ IFileTree = &FileTree{}
|
var _ IFileTree = &FileTree{}
|
||||||
|
|
||||||
func NewFileTree(getFiles func() []*models.File, log *logrus.Entry, showTree bool) *FileTree {
|
func NewFileTree(getFiles func() []*models.File, common *common.Common, showTree bool) *FileTree {
|
||||||
return &FileTree{
|
return &FileTree{
|
||||||
getFiles: getFiles,
|
getFiles: getFiles,
|
||||||
log: log,
|
common: common,
|
||||||
showTree: showTree,
|
showTree: showTree,
|
||||||
filter: DisplayAll,
|
filter: DisplayAll,
|
||||||
collapsedPaths: NewCollapsedPaths(),
|
collapsedPaths: NewCollapsedPaths(),
|
||||||
@@ -168,10 +168,11 @@ func (self *FileTree) GetAllFiles() []*models.File {
|
|||||||
|
|
||||||
func (self *FileTree) SetTree() {
|
func (self *FileTree) SetTree() {
|
||||||
filesForDisplay := self.getFilesForDisplay()
|
filesForDisplay := self.getFilesForDisplay()
|
||||||
|
showRootItem := self.common.UserConfig().Gui.ShowRootItemInFileTree
|
||||||
if self.showTree {
|
if self.showTree {
|
||||||
self.tree = BuildTreeFromFiles(filesForDisplay)
|
self.tree = BuildTreeFromFiles(filesForDisplay, showRootItem)
|
||||||
} else {
|
} else {
|
||||||
self.tree = BuildFlatTreeFromFiles(filesForDisplay)
|
self.tree = BuildFlatTreeFromFiles(filesForDisplay, showRootItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,11 +5,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"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/context/traits"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type IFileTreeViewModel interface {
|
type IFileTreeViewModel interface {
|
||||||
@@ -28,8 +28,8 @@ type FileTreeViewModel struct {
|
|||||||
|
|
||||||
var _ IFileTreeViewModel = &FileTreeViewModel{}
|
var _ IFileTreeViewModel = &FileTreeViewModel{}
|
||||||
|
|
||||||
func NewFileTreeViewModel(getFiles func() []*models.File, log *logrus.Entry, showTree bool) *FileTreeViewModel {
|
func NewFileTreeViewModel(getFiles func() []*models.File, common *common.Common, showTree bool) *FileTreeViewModel {
|
||||||
fileTree := NewFileTree(getFiles, log, showTree)
|
fileTree := NewFileTree(getFiles, common, showTree)
|
||||||
listCursor := traits.NewListCursor(fileTree.Len)
|
listCursor := traits.NewListCursor(fileTree.Len)
|
||||||
return &FileTreeViewModel{
|
return &FileTreeViewModel{
|
||||||
IFileTree: fileTree,
|
IFileTree: fileTree,
|
||||||
|
@@ -25,12 +25,13 @@ func RenderFileTree(
|
|||||||
showFileIcons bool,
|
showFileIcons bool,
|
||||||
showNumstat bool,
|
showNumstat bool,
|
||||||
customIconsConfig *config.CustomIconsConfig,
|
customIconsConfig *config.CustomIconsConfig,
|
||||||
|
showRootItem bool,
|
||||||
) []string {
|
) []string {
|
||||||
collapsedPaths := tree.CollapsedPaths()
|
collapsedPaths := tree.CollapsedPaths()
|
||||||
return renderAux(tree.GetRoot().Raw(), collapsedPaths, -1, -1, func(node *filetree.Node[models.File], treeDepth int, visualDepth int, isCollapsed bool) string {
|
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)
|
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,
|
submoduleConfigs []*models.SubmoduleConfig,
|
||||||
node *filetree.Node[models.File],
|
node *filetree.Node[models.File],
|
||||||
customIconsConfig *config.CustomIconsConfig,
|
customIconsConfig *config.CustomIconsConfig,
|
||||||
|
showRootItem bool,
|
||||||
) string {
|
) string {
|
||||||
name := fileNameAtDepth(node, treeDepth)
|
name := fileNameAtDepth(node, treeDepth, showRootItem)
|
||||||
output := ""
|
output := ""
|
||||||
|
|
||||||
var nameColor style.TextStyle
|
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())
|
splitName := split(node.GetInternalPath())
|
||||||
if depth == 0 && splitName[0] == "." {
|
if depth == 0 && splitName[0] == "." {
|
||||||
if len(splitName) == 1 {
|
if len(splitName) == 1 {
|
||||||
@@ -308,7 +310,7 @@ func fileNameAtDepth(node *filetree.Node[models.File], depth int) string {
|
|||||||
name := join(splitName[depth:])
|
name := join(splitName[depth:])
|
||||||
|
|
||||||
if node.File != nil && node.File.IsRename() {
|
if node.File != nil && node.File.IsRename() {
|
||||||
splitPrevName := split("./" + node.File.PreviousPath)
|
splitPrevName := filetree.SplitFileTreePath(node.File.PreviousPath, showRootItem)
|
||||||
|
|
||||||
prevName := node.File.PreviousPath
|
prevName := node.File.PreviousPath
|
||||||
// if the file has just been renamed inside the same directory, we can shave off
|
// if the file has just been renamed inside the same directory, we can shave off
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/gookit/color"
|
"github.com/gookit/color"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/common"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
@@ -25,6 +26,7 @@ func TestRenderFileTree(t *testing.T) {
|
|||||||
files []*models.File
|
files []*models.File
|
||||||
collapsedPaths []string
|
collapsedPaths []string
|
||||||
showLineChanges bool
|
showLineChanges bool
|
||||||
|
showRootItem bool
|
||||||
expected []string
|
expected []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -37,7 +39,8 @@ func TestRenderFileTree(t *testing.T) {
|
|||||||
files: []*models.File{
|
files: []*models.File{
|
||||||
{Path: "test", ShortStatus: " M", HasStagedChanges: true},
|
{Path: "test", ShortStatus: " M", HasStagedChanges: true},
|
||||||
},
|
},
|
||||||
expected: []string{" M test"},
|
showRootItem: true,
|
||||||
|
expected: []string{" M test"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "numstat",
|
name: "numstat",
|
||||||
@@ -48,6 +51,7 @@ func TestRenderFileTree(t *testing.T) {
|
|||||||
{Path: "test4", ShortStatus: " M", HasStagedChanges: true, LinesAdded: 0, LinesDeleted: 0},
|
{Path: "test4", ShortStatus: " M", HasStagedChanges: true, LinesAdded: 0, LinesDeleted: 0},
|
||||||
},
|
},
|
||||||
showLineChanges: true,
|
showLineChanges: true,
|
||||||
|
showRootItem: true,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"▼ /",
|
"▼ /",
|
||||||
" M test +1 -1",
|
" M test +1 -1",
|
||||||
@@ -66,6 +70,7 @@ func TestRenderFileTree(t *testing.T) {
|
|||||||
{Path: "dir2/file5", ShortStatus: "M ", HasUnstagedChanges: true},
|
{Path: "dir2/file5", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||||
{Path: "file1", ShortStatus: "M ", HasUnstagedChanges: true},
|
{Path: "file1", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: toStringSlice(
|
expected: toStringSlice(
|
||||||
`
|
`
|
||||||
▼ /
|
▼ /
|
||||||
@@ -80,6 +85,30 @@ func TestRenderFileTree(t *testing.T) {
|
|||||||
),
|
),
|
||||||
collapsedPaths: []string{"./dir1"},
|
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)
|
oldColorLevel := color.ForceSetColorLevel(terminfo.ColorLevelNone)
|
||||||
@@ -87,12 +116,14 @@ func TestRenderFileTree(t *testing.T) {
|
|||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
viewModel := filetree.NewFileTree(func() []*models.File { return s.files }, utils.NewDummyLog(), true)
|
common := common.NewDummyCommon()
|
||||||
|
common.UserConfig().Gui.ShowRootItemInFileTree = s.showRootItem
|
||||||
|
viewModel := filetree.NewFileTree(func() []*models.File { return s.files }, common, true)
|
||||||
viewModel.SetTree()
|
viewModel.SetTree()
|
||||||
for _, path := range s.collapsedPaths {
|
for _, path := range s.collapsedPaths {
|
||||||
viewModel.ToggleCollapsed(path)
|
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)
|
assert.EqualValues(t, s.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -104,6 +135,7 @@ func TestRenderCommitFileTree(t *testing.T) {
|
|||||||
root *filetree.FileNode
|
root *filetree.FileNode
|
||||||
files []*models.CommitFile
|
files []*models.CommitFile
|
||||||
collapsedPaths []string
|
collapsedPaths []string
|
||||||
|
showRootItem bool
|
||||||
expected []string
|
expected []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -116,7 +148,8 @@ func TestRenderCommitFileTree(t *testing.T) {
|
|||||||
files: []*models.CommitFile{
|
files: []*models.CommitFile{
|
||||||
{Path: "test", ChangeStatus: "A"},
|
{Path: "test", ChangeStatus: "A"},
|
||||||
},
|
},
|
||||||
expected: []string{"A test"},
|
showRootItem: true,
|
||||||
|
expected: []string{"A test"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "big example",
|
name: "big example",
|
||||||
@@ -128,6 +161,7 @@ func TestRenderCommitFileTree(t *testing.T) {
|
|||||||
{Path: "dir2/file5", ChangeStatus: "M"},
|
{Path: "dir2/file5", ChangeStatus: "M"},
|
||||||
{Path: "file1", ChangeStatus: "M"},
|
{Path: "file1", ChangeStatus: "M"},
|
||||||
},
|
},
|
||||||
|
showRootItem: true,
|
||||||
expected: toStringSlice(
|
expected: toStringSlice(
|
||||||
`
|
`
|
||||||
▼ /
|
▼ /
|
||||||
@@ -142,6 +176,30 @@ func TestRenderCommitFileTree(t *testing.T) {
|
|||||||
),
|
),
|
||||||
collapsedPaths: []string{"./dir1"},
|
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)
|
oldColorLevel := color.ForceSetColorLevel(terminfo.ColorLevelNone)
|
||||||
@@ -151,7 +209,9 @@ func TestRenderCommitFileTree(t *testing.T) {
|
|||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
hashPool := &utils.StringPool{}
|
hashPool := &utils.StringPool{}
|
||||||
|
|
||||||
viewModel := filetree.NewCommitFileTreeViewModel(func() []*models.CommitFile { return s.files }, utils.NewDummyLog(), true)
|
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.SetRef(models.NewCommit(hashPool, models.NewCommitOpts{Hash: "1234"}))
|
||||||
viewModel.SetTree()
|
viewModel.SetTree()
|
||||||
for _, path := range s.collapsedPaths {
|
for _, path := range s.collapsedPaths {
|
||||||
|
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.RememberCommitMessageAfterFail,
|
||||||
file.RenameSimilarityThresholdChange,
|
file.RenameSimilarityThresholdChange,
|
||||||
file.RenamedFiles,
|
file.RenamedFiles,
|
||||||
|
file.RenamedFilesNoRootItem,
|
||||||
file.StageChildrenRangeSelect,
|
file.StageChildrenRangeSelect,
|
||||||
file.StageDeletedRangeSelect,
|
file.StageDeletedRangeSelect,
|
||||||
file.StageRangeSelect,
|
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.",
|
"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
|
"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": {
|
"showNumstatInFilesView": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "If true, show the number of lines changed per file in the Files view",
|
"description": "If true, show the number of lines changed per file in the Files view",
|
||||||
|
Reference in New Issue
Block a user