From bb246377ceb60aba414e08851dde208e827fc451 Mon Sep 17 00:00:00 2001 From: Laurent Nullens Date: Sat, 4 Sep 2021 20:32:55 +0200 Subject: [PATCH] feat(git): display worktree count resolves #913 --- docs/docs/segment-git.mdx | 2 ++ src/environment.go | 16 +++++++++++ src/segment_git.go | 60 +++++++++++++++++++++++---------------- src/segment_path.go | 2 +- src/segment_path_test.go | 5 ++++ themes/schema.json | 14 ++++++++- 6 files changed, 73 insertions(+), 26 deletions(-) diff --git a/docs/docs/segment-git.mdx b/docs/docs/segment-git.mdx index 340c6379..fdd7b960 100644 --- a/docs/docs/segment-git.mdx +++ b/docs/docs/segment-git.mdx @@ -61,10 +61,12 @@ An alternative is to use the [Posh-Git segment][poshgit] - display_status: `boolean` - display the local changes or not - defaults to `false` - display_status_detail: `boolean` - display the local changes in detail or not - defaults to `true` - display_stash_count: `boolean` show stash count or not - defaults to `false` +- display_worktree_count: `boolean` show worktree count or not - defaults to `false` - status_separator_icon: `string` icon/text to display between staging and working area changes - defaults to ` |` - local_working_icon: `string` - the icon to display in front of the working area changes - defaults to `\uF044` - local_staged_icon: `string` - the icon to display in front of the staged area changes - defaults to `\uF046` - stash_count_icon: `string` icon/text to display before the stash context - defaults to `\uF692` +- worktree_count_icon: `string` icon/text to display before the worktree context - defaults to `\uF1BB` ### HEAD context diff --git a/src/environment.go b/src/environment.go index 1f51c3fa..46ce206b 100644 --- a/src/environment.go +++ b/src/environment.go @@ -57,6 +57,7 @@ type environmentInfo interface { hasFilesInDir(dir, pattern string) bool hasFolder(folder string) bool getFileContent(file string) string + getFoldersList(path string) []string getPathSeperator() string getCurrentUser() string isRunningAsRoot() bool @@ -219,6 +220,21 @@ func (env *environment) getFileContent(file string) string { return string(content) } +func (env *environment) getFoldersList(path string) []string { + defer env.tracer.trace(time.Now(), "getFoldersList", path) + content, err := os.ReadDir(path) + if err != nil { + return nil + } + var folderNames []string + for _, s := range content { + if s.IsDir() { + folderNames = append(folderNames, s.Name()) + } + } + return folderNames +} + func (env *environment) getPathSeperator() string { defer env.tracer.trace(time.Now(), "getPathSeperator") return string(os.PathSeparator) diff --git a/src/segment_git.go b/src/segment_git.go index 3857629d..178b3106 100644 --- a/src/segment_git.go +++ b/src/segment_git.go @@ -20,6 +20,7 @@ type gitRepo struct { gitWorkingFolder string // .git working folder, can be different of root if using worktree isWorkTree bool gitRootFolder string // .git root folder + worktreeCount int } type gitStatus struct { @@ -120,6 +121,10 @@ const ( AheadColor Property = "ahead_color" // BranchMaxLength truncates the length of the branch name BranchMaxLength Property = "branch_max_length" + // DisplayWorktreeCount show worktree count or not + DisplayWorktreeCount Property = "display_worktree_count" + // WorktreeCountIcon shows before the worktree context + WorktreeCountIcon Property = "worktree_count_icon" ) func (g *git) enabled() bool { @@ -189,6 +194,9 @@ func (g *git) string() string { if g.repo.stashCount != 0 { fmt.Fprintf(buffer, " %s%d", g.props.getString(StashCountIcon, "\uF692 "), g.repo.stashCount) } + if g.repo.worktreeCount != 0 { + fmt.Fprintf(buffer, " %s%d", g.props.getString(WorktreeCountIcon, "\uf1bb "), g.repo.worktreeCount) + } return buffer.String() } @@ -270,6 +278,9 @@ func (g *git) setGitStatus() { if g.props.getBool(DisplayStashCount, false) { g.repo.stashCount = g.getStashContext() } + if g.props.getBool(DisplayWorktreeCount, false) { + g.repo.worktreeCount = g.getWorktreeContext() + } } func (g *git) SetStatusColor() { @@ -315,30 +326,30 @@ func (g *git) getGitHEADContext(ref string) string { ref = fmt.Sprintf("%s%s", branchIcon, ref) } // rebase - if g.hasGitFolder("rebase-merge") { - head := g.getGitFileContents("rebase-merge/head-name") + if g.env.hasFolder(g.repo.gitWorkingFolder + "/rebase-merge") { + head := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-merge/head-name") origin := strings.Replace(head, "refs/heads/", "", 1) origin = g.truncateBranch(origin) onto := g.getGitRefFileSymbolicName("rebase-merge/onto") onto = g.truncateBranch(onto) - step := g.getGitFileContents("rebase-merge/msgnum") - total := g.getGitFileContents("rebase-merge/end") + step := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-merge/msgnum") + total := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-merge/end") icon := g.props.getString(RebaseIcon, "\uE728 ") return fmt.Sprintf("%s%s%s onto %s%s (%s/%s) at %s", icon, branchIcon, origin, branchIcon, onto, step, total, ref) } - if g.hasGitFolder("rebase-apply") { - head := g.getGitFileContents("rebase-apply/head-name") + if g.env.hasFolder(g.repo.gitWorkingFolder + "/rebase-apply") { + head := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-apply/head-name") origin := strings.Replace(head, "refs/heads/", "", 1) origin = g.truncateBranch(origin) - step := g.getGitFileContents("rebase-apply/next") - total := g.getGitFileContents("rebase-apply/last") + step := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-apply/next") + total := g.getGitFileContents(g.repo.gitWorkingFolder, "rebase-apply/last") icon := g.props.getString(RebaseIcon, "\uE728 ") return fmt.Sprintf("%s%s%s (%s/%s) at %s", icon, branchIcon, origin, step, total, ref) } // merge if g.hasGitFile("MERGE_MSG") && g.hasGitFile("MERGE_HEAD") { icon := g.props.getString(MergeIcon, "\uE727 ") - mergeContext := g.getGitFileContents("MERGE_MSG") + mergeContext := g.getGitFileContents(g.repo.gitWorkingFolder, "MERGE_MSG") matches := findNamedRegexMatch(`Merge branch '(?P.*)' into`, mergeContext) if matches != nil && matches["head"] != "" { branch := g.truncateBranch(matches["head"]) @@ -351,15 +362,15 @@ func (g *git) getGitHEADContext(ref string) string { // reverts then CHERRY_PICK_HEAD/REVERT_HEAD will not exist so we have to read // the todo file. if g.hasGitFile("CHERRY_PICK_HEAD") { - sha := g.getGitFileContents("CHERRY_PICK_HEAD") + sha := g.getGitFileContents(g.repo.gitWorkingFolder, "CHERRY_PICK_HEAD") icon := g.props.getString(CherryPickIcon, "\uE29B ") return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref) } else if g.hasGitFile("REVERT_HEAD") { - sha := g.getGitFileContents("REVERT_HEAD") + sha := g.getGitFileContents(g.repo.gitWorkingFolder, "REVERT_HEAD") icon := g.props.getString(RevertIcon, "\uF0E2 ") return fmt.Sprintf("%s%s onto %s", icon, sha[0:6], ref) } else if g.hasGitFile("sequencer/todo") { - todo := g.getGitFileContents("sequencer/todo") + todo := g.getGitFileContents(g.repo.gitWorkingFolder, "sequencer/todo") matches := findNamedRegexMatch(`^(?Pp|pick|revert)\s+(?P\S+)`, todo) if matches != nil && matches["sha"] != "" { action := matches["action"] @@ -389,25 +400,18 @@ func (g *git) hasGitFile(file string) bool { return g.env.hasFilesInDir(g.repo.gitWorkingFolder, file) } -func (g *git) hasGitFolder(folder string) bool { - path := g.repo.gitWorkingFolder + "/" + folder - return g.env.hasFolder(path) -} - -func (g *git) getGitFileContents(file string) string { - path := g.repo.gitWorkingFolder + "/" + file - content := g.env.getFileContent(path) - return strings.Trim(content, " \r\n") +func (g *git) getGitFileContents(folder, file string) string { + return strings.Trim(g.env.getFileContent(folder+"/"+file), " \r\n") } func (g *git) getGitRefFileSymbolicName(refFile string) string { - ref := g.getGitFileContents(refFile) + ref := g.getGitFileContents(g.repo.gitWorkingFolder, refFile) return g.getGitCommandOutput("name-rev", "--name-only", "--exclude=tags/*", ref) } func (g *git) getPrettyHEADName() string { var ref string - HEAD := g.getGitFileContents("HEAD") + HEAD := g.getGitFileContents(g.repo.gitWorkingFolder, "HEAD") branchPrefix := "ref: refs/heads/" if strings.HasPrefix(HEAD, branchPrefix) { ref = strings.TrimPrefix(HEAD, branchPrefix) @@ -462,7 +466,7 @@ func (g *git) parseGitStats(output []string, working bool) *gitStatus { } func (g *git) getStashContext() int { - stashContent := g.getGitFileContents("logs/refs/stash") + stashContent := g.getGitFileContents(g.repo.gitRootFolder, "logs/refs/stash") if stashContent == "" { return 0 } @@ -470,6 +474,14 @@ func (g *git) getStashContext() int { return len(lines) } +func (g *git) getWorktreeContext() int { + if !g.env.hasFolder(g.repo.gitRootFolder + "/worktrees") { + return 0 + } + worktreeFolders := g.env.getFoldersList(g.repo.gitRootFolder + "/worktrees") + return len(worktreeFolders) +} + func (g *git) parseGitStatusInfo(branchInfo string) map[string]string { var branchRegex = `^## (?P\S+?)(\.{3}(?P\S+?)( \[(?P(ahead (?P\d+)(, )?)?(behind (?P\d+))?(gone)?)])?)?$` return findNamedRegexMatch(branchRegex, branchInfo) diff --git a/src/segment_path.go b/src/segment_path.go index b66d3c62..ffafeb77 100644 --- a/src/segment_path.go +++ b/src/segment_path.go @@ -45,7 +45,7 @@ const ( MappedLocationsEnabled Property = "mapped_locations_enabled" // StackCountEnabled enables the stack count display StackCountEnabled Property = "stack_count_enabled" - // Maximum path depth to display whithout shortening + // MaxDepth Maximum path depth to display whithout shortening MaxDepth Property = "max_depth" ) diff --git a/src/segment_path_test.go b/src/segment_path_test.go index db395bfc..085688ee 100644 --- a/src/segment_path_test.go +++ b/src/segment_path_test.go @@ -49,6 +49,11 @@ func (env *MockedEnvironment) getFileContent(file string) string { return args.String(0) } +func (env *MockedEnvironment) getFoldersList(path string) []string { + args := env.Called(path) + return args.Get(0).([]string) +} + func (env *MockedEnvironment) getPathSeperator() string { args := env.Called(nil) return args.String(0) diff --git a/themes/schema.json b/themes/schema.json index 54b38885..9619c5fa 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -570,6 +570,12 @@ "description": "Display the stash count or not", "default": false }, + "display_worktree_count": { + "type": "boolean", + "title": "Display Worktree Count", + "description": "Display the worktree count or not", + "default": false + }, "status_separator_icon": { "type": "string", "title": "Status Separator Icon", @@ -592,7 +598,13 @@ "type": "string", "title": "Stash Count Icon", "description": "The icon to display before the stash context", - "default": "\uF692" + "default": "\uF692 " + }, + "worktree_count_icon": { + "type": "string", + "title": "Worktree Count Icon", + "description": "The icon to display before the worktree context", + "default": "\uF1bb " }, "commit_icon": { "type": "string",