mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-12 12:55:47 +02:00
Show the number of lines changed per file in working file tree view (#4015)
- **PR Description** Implements Issue #3643 Adds the number of line changes to the end of each file line in the Files view. Also adds the possibility for the user to enable and disable this feature through the UserConfig. <img width="323" alt="screenshot" src="https://github.com/user-attachments/assets/6f818dd4-fbf5-49f2-b338-1b1fcc73f73a"> - **Please check if the PR fulfills these requirements** * [x] Cheatsheets are up-to-date (run `go generate ./...`) * [x] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting)) * [x] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide) * [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation)) * [x] If a new UserConfig entry was added, make sure it can be hot-reloaded (see [here](https://github.com/jesseduffield/lazygit/blob/master/docs/dev/Codebase_Guide.md#using-userconfig)) * [ ] Docs have been updated if necessary * [x] You've read through your own file changes for silly mistakes etc
This commit is contained in:
commit
4a7cf6040e
9 changed files with 174 additions and 33 deletions
|
@ -164,6 +164,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, show the number of lines changed per file in the Files view
|
||||||
|
showNumstatInFilesView: false
|
||||||
|
|
||||||
# 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
|
||||||
showRandomTip: true
|
showRandomTip: true
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package git_commands
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||||
|
@ -48,6 +49,14 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
||||||
}
|
}
|
||||||
files := []*models.File{}
|
files := []*models.File{}
|
||||||
|
|
||||||
|
fileDiffs := map[string]FileDiff{}
|
||||||
|
if self.GitCommon.Common.UserConfig().Gui.ShowNumstatInFilesView {
|
||||||
|
fileDiffs, err = self.getFileDiffs()
|
||||||
|
if err != nil {
|
||||||
|
self.Log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, status := range statuses {
|
for _, status := range statuses {
|
||||||
if strings.HasPrefix(status.StatusString, "warning") {
|
if strings.HasPrefix(status.StatusString, "warning") {
|
||||||
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
self.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
||||||
|
@ -60,6 +69,11 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
||||||
DisplayString: status.StatusString,
|
DisplayString: status.StatusString,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if diff, ok := fileDiffs[status.Name]; ok {
|
||||||
|
file.LinesAdded = diff.LinesAdded
|
||||||
|
file.LinesDeleted = diff.LinesDeleted
|
||||||
|
}
|
||||||
|
|
||||||
models.SetStatusFields(file, status.Change)
|
models.SetStatusFields(file, status.Change)
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
|
@ -87,6 +101,45 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileDiff struct {
|
||||||
|
LinesAdded int
|
||||||
|
LinesDeleted int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fileLoader *FileLoader) getFileDiffs() (map[string]FileDiff, error) {
|
||||||
|
diffs, err := fileLoader.gitDiffNumStat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
splitLines := strings.Split(diffs, "\x00")
|
||||||
|
|
||||||
|
fileDiffs := map[string]FileDiff{}
|
||||||
|
for _, line := range splitLines {
|
||||||
|
splitLine := strings.Split(line, "\t")
|
||||||
|
if len(splitLine) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
linesAdded, err := strconv.Atoi(splitLine[0])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
linesDeleted, err := strconv.Atoi(splitLine[1])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := splitLine[2]
|
||||||
|
fileDiffs[fileName] = FileDiff{
|
||||||
|
LinesAdded: linesAdded,
|
||||||
|
LinesDeleted: linesDeleted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileDiffs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GitStatus returns the file status of the repo
|
// GitStatus returns the file status of the repo
|
||||||
type GitStatusOptions struct {
|
type GitStatusOptions struct {
|
||||||
NoRenames bool
|
NoRenames bool
|
||||||
|
@ -100,6 +153,16 @@ type FileStatus struct {
|
||||||
PreviousName string
|
PreviousName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fileLoader *FileLoader) gitDiffNumStat() (string, error) {
|
||||||
|
return fileLoader.cmd.New(
|
||||||
|
NewGitCmd("diff").
|
||||||
|
Arg("--numstat").
|
||||||
|
Arg("-z").
|
||||||
|
Arg("HEAD").
|
||||||
|
ToArgv(),
|
||||||
|
).DontLog().RunWithOutput()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||||
cmdArgs := NewGitCmd("status").
|
cmdArgs := NewGitCmd("status").
|
||||||
Arg(opts.UntrackedFilesArg).
|
Arg(opts.UntrackedFilesArg).
|
||||||
|
|
|
@ -14,26 +14,32 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
testName string
|
testName string
|
||||||
similarityThreshold int
|
similarityThreshold int
|
||||||
runner oscommands.ICmdObjRunner
|
runner oscommands.ICmdObjRunner
|
||||||
|
showNumstatInFilesView bool
|
||||||
expectedFiles []*models.File
|
expectedFiles []*models.File
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"No files found",
|
testName: "No files found",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "", nil),
|
||||||
[]*models.File{},
|
expectedFiles: []*models.File{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Several files found",
|
testName: "Several files found",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||||
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
|
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
|
||||||
nil,
|
nil,
|
||||||
|
).
|
||||||
|
ExpectGitArgs([]string{"diff", "--numstat", "-z", "HEAD"},
|
||||||
|
"4\t1\tfile1.txt\x001\t0\tfile2.txt\x002\t2\tfile3.txt\x000\t2\tfile4.txt\x002\t2\tfile5.txt",
|
||||||
|
nil,
|
||||||
),
|
),
|
||||||
[]*models.File{
|
showNumstatInFilesView: true,
|
||||||
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "file1.txt",
|
Name: "file1.txt",
|
||||||
HasStagedChanges: true,
|
HasStagedChanges: true,
|
||||||
|
@ -45,6 +51,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "MM file1.txt",
|
DisplayString: "MM file1.txt",
|
||||||
ShortStatus: "MM",
|
ShortStatus: "MM",
|
||||||
|
LinesAdded: 4,
|
||||||
|
LinesDeleted: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file3.txt",
|
Name: "file3.txt",
|
||||||
|
@ -57,6 +65,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "A file3.txt",
|
DisplayString: "A file3.txt",
|
||||||
ShortStatus: "A ",
|
ShortStatus: "A ",
|
||||||
|
LinesAdded: 2,
|
||||||
|
LinesDeleted: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file2.txt",
|
Name: "file2.txt",
|
||||||
|
@ -69,6 +79,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "AM file2.txt",
|
DisplayString: "AM file2.txt",
|
||||||
ShortStatus: "AM",
|
ShortStatus: "AM",
|
||||||
|
LinesAdded: 1,
|
||||||
|
LinesDeleted: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file4.txt",
|
Name: "file4.txt",
|
||||||
|
@ -81,6 +93,8 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
HasInlineMergeConflicts: false,
|
HasInlineMergeConflicts: false,
|
||||||
DisplayString: "?? file4.txt",
|
DisplayString: "?? file4.txt",
|
||||||
ShortStatus: "??",
|
ShortStatus: "??",
|
||||||
|
LinesAdded: 0,
|
||||||
|
LinesDeleted: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "file5.txt",
|
Name: "file5.txt",
|
||||||
|
@ -93,15 +107,17 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
HasInlineMergeConflicts: true,
|
HasInlineMergeConflicts: true,
|
||||||
DisplayString: "UU file5.txt",
|
DisplayString: "UU file5.txt",
|
||||||
ShortStatus: "UU",
|
ShortStatus: "UU",
|
||||||
|
LinesAdded: 2,
|
||||||
|
LinesDeleted: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"File with new line char",
|
testName: "File with new line char",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"}, "MM a\nb.txt", nil),
|
||||||
[]*models.File{
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "a\nb.txt",
|
Name: "a\nb.txt",
|
||||||
HasStagedChanges: true,
|
HasStagedChanges: true,
|
||||||
|
@ -117,14 +133,14 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Renamed files",
|
testName: "Renamed files",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||||
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
|
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
[]*models.File{
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "after1.txt",
|
Name: "after1.txt",
|
||||||
PreviousName: "before1.txt",
|
PreviousName: "before1.txt",
|
||||||
|
@ -154,14 +170,14 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"File with arrow in name",
|
testName: "File with arrow in name",
|
||||||
50,
|
similarityThreshold: 50,
|
||||||
oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z", "--find-renames=50%"},
|
||||||
`?? a -> b.txt`,
|
`?? a -> b.txt`,
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
[]*models.File{
|
expectedFiles: []*models.File{
|
||||||
{
|
{
|
||||||
Name: "a -> b.txt",
|
Name: "a -> b.txt",
|
||||||
HasStagedChanges: false,
|
HasStagedChanges: false,
|
||||||
|
@ -185,8 +201,14 @@ func TestFileGetStatusFiles(t *testing.T) {
|
||||||
appState := &config.AppState{}
|
appState := &config.AppState{}
|
||||||
appState.RenameSimilarityThreshold = s.similarityThreshold
|
appState.RenameSimilarityThreshold = s.similarityThreshold
|
||||||
|
|
||||||
|
userConfig := &config.UserConfig{
|
||||||
|
Gui: config.GuiConfig{
|
||||||
|
ShowNumstatInFilesView: s.showNumstatInFilesView,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
loader := &FileLoader{
|
loader := &FileLoader{
|
||||||
GitCommon: buildGitCommon(commonDeps{appState: appState}),
|
GitCommon: buildGitCommon(commonDeps{appState: appState, userConfig: userConfig}),
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
|
config: &FakeFileLoaderConfig{showUntrackedFiles: "yes"},
|
||||||
getFileType: func(string) string { return "file" },
|
getFileType: func(string) string { return "file" },
|
||||||
|
|
|
@ -19,6 +19,8 @@ type File struct {
|
||||||
HasInlineMergeConflicts bool
|
HasInlineMergeConflicts bool
|
||||||
DisplayString string
|
DisplayString string
|
||||||
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
|
ShortStatus string // e.g. 'AD', ' A', 'M ', '??'
|
||||||
|
LinesDeleted int
|
||||||
|
LinesAdded int
|
||||||
|
|
||||||
// If true, this must be a worktree folder
|
// If true, this must be a worktree folder
|
||||||
IsWorktree bool
|
IsWorktree bool
|
||||||
|
|
|
@ -109,6 +109,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, 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
|
// If true, show a random tip in the command log when Lazygit starts
|
||||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||||
// If true, show the command log
|
// If true, show the command log
|
||||||
|
@ -714,6 +716,7 @@ func GetDefaultConfig() *UserConfig {
|
||||||
ShowBottomLine: true,
|
ShowBottomLine: true,
|
||||||
ShowPanelJumps: true,
|
ShowPanelJumps: true,
|
||||||
ShowFileTree: true,
|
ShowFileTree: true,
|
||||||
|
ShowNumstatInFilesView: false,
|
||||||
ShowRandomTip: true,
|
ShowRandomTip: true,
|
||||||
ShowIcons: false,
|
ShowIcons: false,
|
||||||
NerdFontsVersion: "",
|
NerdFontsVersion: "",
|
||||||
|
|
|
@ -30,7 +30,8 @@ func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||||
|
|
||||||
getDisplayStrings := func(_ int, _ int) [][]string {
|
getDisplayStrings := func(_ int, _ int) [][]string {
|
||||||
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
showFileIcons := icons.IsIconEnabled() && c.UserConfig().Gui.ShowFileIcons
|
||||||
lines := presentation.RenderFileTree(viewModel, c.Model().Submodules, showFileIcons)
|
showNumstat := c.UserConfig().Gui.ShowNumstatInFilesView
|
||||||
|
lines := presentation.RenderFileTree(viewModel, c.Model().Submodules, showFileIcons, showNumstat)
|
||||||
return lo.Map(lines, func(line string, _ int) []string {
|
return lo.Map(lines, func(line string, _ int) []string {
|
||||||
return []string{line}
|
return []string{line}
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,12 +22,13 @@ func RenderFileTree(
|
||||||
tree filetree.IFileTree,
|
tree filetree.IFileTree,
|
||||||
submoduleConfigs []*models.SubmoduleConfig,
|
submoduleConfigs []*models.SubmoduleConfig,
|
||||||
showFileIcons bool,
|
showFileIcons bool,
|
||||||
|
showNumstat 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, showFileIcons, submoduleConfigs, node)
|
return getFileLine(isCollapsed, fileNode.GetHasUnstagedChanges(), fileNode.GetHasStagedChanges(), treeDepth, visualDepth, showNumstat, showFileIcons, submoduleConfigs, node)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +112,7 @@ func getFileLine(
|
||||||
hasStagedChanges bool,
|
hasStagedChanges bool,
|
||||||
treeDepth int,
|
treeDepth int,
|
||||||
visualDepth int,
|
visualDepth int,
|
||||||
|
showNumstat,
|
||||||
showFileIcons bool,
|
showFileIcons bool,
|
||||||
submoduleConfigs []*models.SubmoduleConfig,
|
submoduleConfigs []*models.SubmoduleConfig,
|
||||||
node *filetree.Node[models.File],
|
node *filetree.Node[models.File],
|
||||||
|
@ -165,6 +167,12 @@ func getFileLine(
|
||||||
output += theme.DefaultTextColor.Sprint(" (submodule)")
|
output += theme.DefaultTextColor.Sprint(" (submodule)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if file != nil && showNumstat {
|
||||||
|
if lineChanges := formatLineChanges(file.LinesAdded, file.LinesDeleted); lineChanges != "" {
|
||||||
|
output += " " + lineChanges
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +194,23 @@ func formatFileStatus(file *models.File, restColor style.TextStyle) string {
|
||||||
return firstCharCl.Sprint(firstChar) + secondCharCl.Sprint(secondChar)
|
return firstCharCl.Sprint(firstChar) + secondCharCl.Sprint(secondChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatLineChanges(linesAdded, linesDeleted int) string {
|
||||||
|
output := ""
|
||||||
|
|
||||||
|
if linesAdded != 0 {
|
||||||
|
output += style.FgGreen.Sprintf("+%d", linesAdded)
|
||||||
|
}
|
||||||
|
|
||||||
|
if linesDeleted != 0 {
|
||||||
|
if output != "" {
|
||||||
|
output += " "
|
||||||
|
}
|
||||||
|
output += style.FgRed.Sprintf("-%d", linesDeleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
func getCommitFileLine(
|
func getCommitFileLine(
|
||||||
isCollapsed bool,
|
isCollapsed bool,
|
||||||
treeDepth int,
|
treeDepth int,
|
||||||
|
|
|
@ -23,6 +23,7 @@ func TestRenderFileTree(t *testing.T) {
|
||||||
root *filetree.FileNode
|
root *filetree.FileNode
|
||||||
files []*models.File
|
files []*models.File
|
||||||
collapsedPaths []string
|
collapsedPaths []string
|
||||||
|
showLineChanges bool
|
||||||
expected []string
|
expected []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -37,6 +38,22 @@ func TestRenderFileTree(t *testing.T) {
|
||||||
},
|
},
|
||||||
expected: []string{" M test"},
|
expected: []string{" M test"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "numstat",
|
||||||
|
files: []*models.File{
|
||||||
|
{Name: "test", ShortStatus: " M", HasStagedChanges: true, LinesAdded: 1, LinesDeleted: 1},
|
||||||
|
{Name: "test2", ShortStatus: " M", HasStagedChanges: true, LinesAdded: 1},
|
||||||
|
{Name: "test3", ShortStatus: " M", HasStagedChanges: true, LinesDeleted: 1},
|
||||||
|
{Name: "test4", ShortStatus: " M", HasStagedChanges: true, LinesAdded: 0, LinesDeleted: 0},
|
||||||
|
},
|
||||||
|
showLineChanges: true,
|
||||||
|
expected: []string{
|
||||||
|
" M test +1 -1",
|
||||||
|
" M test2 +1",
|
||||||
|
" M test3 -1",
|
||||||
|
" M test4",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "big example",
|
name: "big example",
|
||||||
files: []*models.File{
|
files: []*models.File{
|
||||||
|
@ -72,7 +89,7 @@ M file1
|
||||||
for _, path := range s.collapsedPaths {
|
for _, path := range s.collapsedPaths {
|
||||||
viewModel.ToggleCollapsed(path)
|
viewModel.ToggleCollapsed(path)
|
||||||
}
|
}
|
||||||
result := RenderFileTree(viewModel, nil, false)
|
result := RenderFileTree(viewModel, nil, false, s.showLineChanges)
|
||||||
assert.EqualValues(t, s.expected, result)
|
assert.EqualValues(t, s.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -293,6 +293,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
|
||||||
},
|
},
|
||||||
|
"showNumstatInFilesView": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If true, show the number of lines changed per file in the Files view",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"showRandomTip": {
|
"showRandomTip": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "If true, show a random tip in the command log when Lazygit starts",
|
"description": "If true, show a random tip in the command log when Lazygit starts",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue