From 162cab391821f30eb1e921595c5b5fb6da67d80c Mon Sep 17 00:00:00 2001 From: Thibault Jamet Date: Thu, 12 Dec 2019 22:29:29 +0100 Subject: [PATCH] Create segments in parallel Powerline being integrated directly in users shell, the perceived performance and time to render and display the prompt is a critical part of the program. As discussed in #121, there are some integrations that requires executing sub-programs and hence takes longer to render. In order to improve the perceived speed, generate the segments data in parallel, before sorting them to ensure the order the user required on the command line. After my testing, I am seeing that using modules user,host,ssh,docker,kube,cwd,perms,git improves from 120 ms without the update to 30 ms with the update. --- main.go | 4 ++- powerline.go | 59 ++++++++++++++++++++++++++++------ powerline/powerline.go | 3 ++ segment-aws.go | 8 +++-- segment-cwd.go | 12 ++++--- segment-docker.go | 8 +++-- segment-dotenv.go | 8 +++-- segment-duration.go | 22 +++++++------ segment-exitcode.go | 8 +++-- segment-git.go | 41 ++++++++++++----------- segment-gitlite.go | 13 ++++---- segment-hg.go | 8 +++-- segment-hostname.go | 7 ++-- segment-jobs.go | 8 +++-- segment-kube.go | 11 ++++--- segment-load.go | 9 +++--- segment-newline.go | 6 ++-- segment-nix-shell.go | 9 +++--- segment-node.go | 8 +++-- segment-perlbrew.go | 9 +++--- segment-plenv.go | 12 +++---- segment-plugin.go | 11 +++---- segment-readonly.go | 8 +++-- segment-readonly_windows.go | 8 +++-- segment-root.go | 7 ++-- segment-shellvar.go | 12 ++++--- segment-ssh.go | 8 +++-- segment-subversion.go | 37 ++++++++++++--------- segment-termtitle.go | 9 +++--- segment-terraform_workspace.go | 9 +++--- segment-time.go | 7 ++-- segment-username.go | 7 ++-- segment-vgo.go | 12 ++++--- segment-virtualenv.go | 12 ++++--- 34 files changed, 261 insertions(+), 159 deletions(-) diff --git a/main.go b/main.go index 93ba6ab3..b1941d35 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,8 @@ import ( "io/ioutil" "os" "strings" + + pwl "github.com/justjanne/powerline-go/powerline" ) type alignment int @@ -88,7 +90,7 @@ func getValidCwd() string { return cwd } -var modules = map[string]func(*powerline){ +var modules = map[string]func(*powerline) []pwl.Segment{ "aws": segmentAWS, "cwd": segmentCwd, "docker": segmentDocker, diff --git a/powerline.go b/powerline.go index 8e66ad58..d7cda3a4 100644 --- a/powerline.go +++ b/powerline.go @@ -6,6 +6,7 @@ import ( "os" "strconv" "strings" + "sync" pwl "github.com/justjanne/powerline-go/powerline" "github.com/mattn/go-runewidth" @@ -43,6 +44,11 @@ type powerline struct { appendEastAsianPadding int } +type prioritizedSegments struct { + i int + segs []pwl.Segment +} + func newPowerline(args args, cwd string, priorities map[string]int, align alignment) *powerline { p := new(powerline) p.args = args @@ -82,17 +88,48 @@ func newPowerline(args args, cwd string, priorities map[string]int, align alignm } else { mods = *args.ModulesRight } - for _, module := range strings.Split(mods, ",") { - elem, ok := modules[module] - if ok { - elem(p) - } else { - if ok := segmentPlugin(p, module); !ok { - println("Module not found: " + module) + initSegments(p, strings.Split(mods, ",")) + + return p +} + +func initSegments(p *powerline, mods []string) { + orderedSegments := map[int][]pwl.Segment{} + c := make(chan prioritizedSegments, len(mods)) + wg := sync.WaitGroup{} + for i, module := range mods { + wg.Add(1) + go func(w *sync.WaitGroup, i int, module string, c chan prioritizedSegments) { + elem, ok := modules[module] + if ok { + c <- prioritizedSegments{ + i: i, + segs: elem(p), + } + } else { + s, ok := segmentPlugin(p, module) + if ok { + c <- prioritizedSegments{ + i: i, + segs: s, + } + } else { + println("Module not found: " + module) + } } + wg.Done() + }(&wg, i, module, c) + } + wg.Wait() + close(c) + for s := range c { + orderedSegments[s.i] = s.segs + } + for i := 0; i < len(mods); i++ { + for _, seg := range orderedSegments[i] { + p.appendSegment(seg.Name, seg) } } - return p } func (p *powerline) color(prefix string, code uint8) string { @@ -128,7 +165,11 @@ func (p *powerline) appendSegment(origin string, segment pwl.Segment) { priority, _ := p.priorities[origin] segment.Priority += priority segment.Width = segment.ComputeWidth(*p.args.Condensed) - p.Segments[p.curSegment] = append(p.Segments[p.curSegment], segment) + if segment.NewLine { + p.newRow() + } else { + p.Segments[p.curSegment] = append(p.Segments[p.curSegment], segment) + } } func (p *powerline) newRow() { diff --git a/powerline/powerline.go b/powerline/powerline.go index 1bac59a4..d0f6234d 100644 --- a/powerline/powerline.go +++ b/powerline/powerline.go @@ -6,6 +6,7 @@ import ( // Segment describes an information to display on the command line prompt type Segment struct { + Name string // Content is the text to be displayed on the command line prompt Content string // Foreground is the text color (see https://misc.flogisoft.com/bash/tip_colors_and_formatting#background1) @@ -21,6 +22,8 @@ type Segment struct { // HideSeparators indicated not to display any separator with next segment. HideSeparators bool Width int + // NewLine defines a newline segment to break the powerline in multi lines + NewLine bool } func (s Segment) ComputeWidth(condensed bool) int { diff --git a/segment-aws.go b/segment-aws.go index 776e6474..e039f62b 100644 --- a/segment-aws.go +++ b/segment-aws.go @@ -5,7 +5,7 @@ import ( "os" ) -func segmentAWS(p *powerline) { +func segmentAWS(p *powerline) []pwl.Segment { profile := os.Getenv("AWS_PROFILE") region := os.Getenv("AWS_DEFAULT_REGION") if profile != "" { @@ -13,10 +13,12 @@ func segmentAWS(p *powerline) { if region != "" { r = " (" + region + ")" } - p.appendSegment("aws", pwl.Segment{ + return []pwl.Segment{{ + Name: "aws", Content: profile + r, Foreground: p.theme.AWSFg, Background: p.theme.AWSBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-cwd.go b/segment-cwd.go index 28160dc2..bc914b4a 100644 --- a/segment-cwd.go +++ b/segment-cwd.go @@ -156,7 +156,7 @@ func getColor(p *powerline, pathSegment pathSegment, isLastDir bool) (uint8, uin return p.theme.PathFg, p.theme.PathBg, false } -func segmentCwd(p *powerline) { +func segmentCwd(p *powerline) (segments []pwl.Segment) { cwd := p.cwd if cwd == "" { cwd, _ = os.LookupEnv("PWD") @@ -168,7 +168,8 @@ func segmentCwd(p *powerline) { cwd = "~" + cwd[len(home):] } - p.appendSegment("cwd", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "cwd", Content: cwd, Foreground: p.theme.CwdFg, Background: p.theme.PathBg, @@ -222,12 +223,13 @@ func segmentCwd(p *powerline) { } } - origin := "cwd-path" + segment.Name = "cwd-path" if isLastDir { - origin = "cwd" + segment.Name = "cwd" } - p.appendSegment(origin, segment) + segments = append(segments, segment) } } + return segments } diff --git a/segment-docker.go b/segment-docker.go index ce77fa45..f7fa2da0 100644 --- a/segment-docker.go +++ b/segment-docker.go @@ -7,7 +7,7 @@ import ( pwl "github.com/justjanne/powerline-go/powerline" ) -func segmentDocker(p *powerline) { +func segmentDocker(p *powerline) []pwl.Segment { var docker string dockerMachineName, _ := os.LookupEnv("DOCKER_MACHINE_NAME") dockerHost, _ := os.LookupEnv("DOCKER_HOST") @@ -22,10 +22,12 @@ func segmentDocker(p *powerline) { } if docker != "" { - p.appendSegment("docker", pwl.Segment{ + return []pwl.Segment{{ + Name: "docker", Content: docker, Foreground: p.theme.DockerMachineFg, Background: p.theme.DockerMachineBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-dotenv.go b/segment-dotenv.go index 5522afbd..c3c28b71 100644 --- a/segment-dotenv.go +++ b/segment-dotenv.go @@ -5,7 +5,7 @@ import ( "os" ) -func segmentDotEnv(p *powerline) { +func segmentDotEnv(p *powerline) []pwl.Segment { files := []string{".env", ".envrc"} dotEnv := false for _, file := range files { @@ -16,10 +16,12 @@ func segmentDotEnv(p *powerline) { } } if dotEnv { - p.appendSegment("dotenv", pwl.Segment{ + return []pwl.Segment{{ + Name: "dotenv", Content: "\u2235", Foreground: p.theme.DotEnvFg, Background: p.theme.DotEnvBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-duration.go b/segment-duration.go index 26e70650..de5b2b70 100644 --- a/segment-duration.go +++ b/segment-duration.go @@ -26,14 +26,14 @@ const ( hours int64 = minutes * 60 ) -func segmentDuration(p *powerline) { +func segmentDuration(p *powerline) []pwl.Segment { if p.args.Duration == nil || *p.args.Duration == "" { - p.appendSegment("duration", pwl.Segment{ + return []pwl.Segment{{ + Name: "duration", Content: "No duration", Foreground: p.theme.DurationFg, Background: p.theme.DurationBg, - }) - return + }} } durationValue := strings.Trim(*p.args.Duration, "'\"") @@ -44,16 +44,16 @@ func segmentDuration(p *powerline) { durationFloat, err := strconv.ParseFloat(durationValue, 64) durationMinFloat, _ := strconv.ParseFloat(durationMinValue, 64) if err != nil { - p.appendSegment("duration", pwl.Segment{ + return []pwl.Segment{{ + Name: "duration", Content: fmt.Sprintf("Failed to convert '%s' to a number", *p.args.Duration), Foreground: p.theme.DurationFg, Background: p.theme.DurationBg, - }) - return + }} } if durationFloat < durationMinFloat { - return + return []pwl.Segment{} } duration := time.Duration(durationFloat * float64(time.Second.Nanoseconds())) @@ -88,10 +88,12 @@ func segmentDuration(p *powerline) { content = fmt.Sprintf("%d\u00B5s", ns/microseconds) } - p.appendSegment("duration", pwl.Segment{ + return []pwl.Segment{{ + Name: "duration", Content: content, Foreground: p.theme.DurationFg, Background: p.theme.DurationBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-exitcode.go b/segment-exitcode.go index ca4b3fff..2b9a8884 100644 --- a/segment-exitcode.go +++ b/segment-exitcode.go @@ -65,7 +65,7 @@ func getMeaningFromExitCode(exitCode int) string { } } -func segmentExitCode(p *powerline) { +func segmentExitCode(p *powerline) []pwl.Segment { var meaning string if *p.args.PrevError != 0 { if *p.args.NumericExitCodes { @@ -73,10 +73,12 @@ func segmentExitCode(p *powerline) { } else { meaning = getMeaningFromExitCode(*p.args.PrevError) } - p.appendSegment("exit", pwl.Segment{ + return []pwl.Segment{{ + Name: "exit", Content: meaning, Foreground: p.theme.CmdFailedFg, Background: p.theme.CmdFailedBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-git.go b/segment-git.go index 513cc05f..990aa08f 100644 --- a/segment-git.go +++ b/segment-git.go @@ -24,24 +24,27 @@ func (r repoStats) dirty() bool { return r.untracked+r.notStaged+r.staged+r.conflicted > 0 } -func addRepoStatsSegment(p *powerline, nChanges int, symbol string, foreground uint8, background uint8) { +func addRepoStatsSegment(nChanges int, symbol string, foreground uint8, background uint8) []pwl.Segment { if nChanges > 0 { - p.appendSegment("git-status", pwl.Segment{ + return []pwl.Segment{{ + Name: "git-status", Content: fmt.Sprintf("%d%s", nChanges, symbol), Foreground: foreground, Background: background, - }) + }} } + return []pwl.Segment{} } -func (r repoStats) addToPowerline(p *powerline) { - addRepoStatsSegment(p, r.ahead, p.symbolTemplates.RepoAhead, p.theme.GitAheadFg, p.theme.GitAheadBg) - addRepoStatsSegment(p, r.behind, p.symbolTemplates.RepoBehind, p.theme.GitBehindFg, p.theme.GitBehindBg) - addRepoStatsSegment(p, r.staged, p.symbolTemplates.RepoStaged, p.theme.GitStagedFg, p.theme.GitStagedBg) - addRepoStatsSegment(p, r.notStaged, p.symbolTemplates.RepoNotStaged, p.theme.GitNotStagedFg, p.theme.GitNotStagedBg) - addRepoStatsSegment(p, r.untracked, p.symbolTemplates.RepoUntracked, p.theme.GitUntrackedFg, p.theme.GitUntrackedBg) - addRepoStatsSegment(p, r.conflicted, p.symbolTemplates.RepoConflicted, p.theme.GitConflictedFg, p.theme.GitConflictedBg) - addRepoStatsSegment(p, r.stashed, p.symbolTemplates.RepoStashed, p.theme.GitStashedFg, p.theme.GitStashedBg) +func (r repoStats) GitSegments(p *powerline) (segments []pwl.Segment) { + segments = append(segments, addRepoStatsSegment(r.ahead, p.symbolTemplates.RepoAhead, p.theme.GitAheadFg, p.theme.GitAheadBg)...) + segments = append(segments, addRepoStatsSegment(r.behind, p.symbolTemplates.RepoBehind, p.theme.GitBehindFg, p.theme.GitBehindBg)...) + segments = append(segments, addRepoStatsSegment(r.staged, p.symbolTemplates.RepoStaged, p.theme.GitStagedFg, p.theme.GitStagedBg)...) + segments = append(segments, addRepoStatsSegment(r.notStaged, p.symbolTemplates.RepoNotStaged, p.theme.GitNotStagedFg, p.theme.GitNotStagedBg)...) + segments = append(segments, addRepoStatsSegment(r.untracked, p.symbolTemplates.RepoUntracked, p.theme.GitUntrackedFg, p.theme.GitUntrackedBg)...) + segments = append(segments, addRepoStatsSegment(r.conflicted, p.symbolTemplates.RepoConflicted, p.theme.GitConflictedFg, p.theme.GitConflictedBg)...) + segments = append(segments, addRepoStatsSegment(r.stashed, p.symbolTemplates.RepoStashed, p.theme.GitStashedFg, p.theme.GitStashedBg)...) + return } var branchRegex = regexp.MustCompile(`^## (?P\S+?)(\.{3}(?P\S+?)( \[(ahead (?P\d+)(, )?)?(behind (?P\d+))?])?)?$`) @@ -124,21 +127,21 @@ func parseGitStats(status []string) repoStats { return stats } -func segmentGit(p *powerline) { +func segmentGit(p *powerline) []pwl.Segment { if len(p.ignoreRepos) > 0 { out, err := runGitCommand("git", "rev-parse", "--show-toplevel") if err != nil { - return + return []pwl.Segment{} } out = strings.TrimSpace(out) if p.ignoreRepos[out] { - return + return []pwl.Segment{} } } out, err := runGitCommand("git", "status", "--porcelain", "-b", "--ignore-submodules") if err != nil { - return + return []pwl.Segment{} } status := strings.Split(out, "\n") @@ -172,10 +175,12 @@ func segmentGit(p *powerline) { stats.stashed = len(strings.Split(out, "\n")) - 1 } - p.appendSegment("git-branch", pwl.Segment{ + segments := []pwl.Segment{{ + Name: "git-branch", Content: branch, Foreground: foreground, Background: background, - }) - stats.addToPowerline(p) + }} + segments = append(segments, stats.GitSegments(p)...) + return segments } diff --git a/segment-gitlite.go b/segment-gitlite.go index 113262fe..835fc700 100644 --- a/segment-gitlite.go +++ b/segment-gitlite.go @@ -5,21 +5,21 @@ import ( "strings" ) -func segmentGitLite(p *powerline) { +func segmentGitLite(p *powerline) []pwl.Segment { if len(p.ignoreRepos) > 0 { out, err := runGitCommand("git", "rev-parse", "--show-toplevel") if err != nil { - return + return []pwl.Segment{} } out = strings.TrimSpace(out) if p.ignoreRepos[out] { - return + return []pwl.Segment{} } } out, err := runGitCommand("git", "rev-parse", "--abbrev-ref", "HEAD") if err != nil { - return + return []pwl.Segment{} } status := strings.TrimSpace(out) @@ -31,9 +31,10 @@ func segmentGitLite(p *powerline) { branch = getGitDetachedBranch(p) } - p.appendSegment("git-branch", pwl.Segment{ + return []pwl.Segment{{ + Name: "git-branch", Content: branch, Foreground: p.theme.RepoCleanFg, Background: p.theme.RepoCleanBg, - }) + }} } diff --git a/segment-hg.go b/segment-hg.go index 949f7ac2..1ecfcb70 100644 --- a/segment-hg.go +++ b/segment-hg.go @@ -30,7 +30,7 @@ func getHgStatus() (bool, bool, bool) { return hasModifiedFiles, hasUntrackedFiles, hasMissingFiles } -func segmentHg(p *powerline) { +func segmentHg(p *powerline) []pwl.Segment { out, _ := exec.Command("hg", "branch").Output() output := strings.SplitN(string(out), "\n", 2) if len(output) > 0 && output[0] != "" { @@ -61,10 +61,12 @@ func segmentHg(p *powerline) { content = fmt.Sprintf(branch) } - p.appendSegment("hg", pwl.Segment{ + return []pwl.Segment{{ + Name: "hg", Content: content, Foreground: foreground, Background: background, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-hostname.go b/segment-hostname.go index 89ad052f..9beea67a 100644 --- a/segment-hostname.go +++ b/segment-hostname.go @@ -18,7 +18,7 @@ func getMd5(text string) []byte { return hasher.Sum(nil) } -func segmentHost(p *powerline) { +func segmentHost(p *powerline) []pwl.Segment { var hostPrompt string var foreground, background uint8 if *p.args.ColorizeHostname { @@ -41,9 +41,10 @@ func segmentHost(p *powerline) { background = p.theme.HostnameBg } - p.appendSegment("host", pwl.Segment{ + return []pwl.Segment{{ + Name: "host", Content: hostPrompt, Foreground: foreground, Background: background, - }) + }} } diff --git a/segment-jobs.go b/segment-jobs.go index 3bcbd590..106d7748 100644 --- a/segment-jobs.go +++ b/segment-jobs.go @@ -9,7 +9,7 @@ import ( "strings" ) -func segmentJobs(p *powerline) { +func segmentJobs(p *powerline) []pwl.Segment { nJobs := -1 ppid := os.Getppid() @@ -29,10 +29,12 @@ func segmentJobs(p *powerline) { } if nJobs > 0 { - p.appendSegment("jobs", pwl.Segment{ + return []pwl.Segment{{ + Name: "jobs", Content: fmt.Sprintf("%d", nJobs), Foreground: p.theme.JobsFg, Background: p.theme.JobsBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-kube.go b/segment-kube.go index 01dc202e..9ca0ab45 100644 --- a/segment-kube.go +++ b/segment-kube.go @@ -55,7 +55,7 @@ func readKubeConfig(config *KubeConfig, path string) (err error) { return } -func segmentKube(p *powerline) { +func segmentKube(p *powerline) []pwl.Segment { paths := append(strings.Split(os.Getenv("KUBECONFIG"), ":"), path.Join(homePath(), ".kube", "config")) config := &KubeConfig{} for _, configPath := range paths { @@ -96,12 +96,13 @@ func segmentKube(p *powerline) { if arnMatches := arnRe.FindStringSubmatch(cluster); arnMatches != nil && *p.args.ShortenEKSNames { cluster = arnMatches[1] } - + segments := []pwl.Segment{} // Only draw the icon once kubeIconHasBeenDrawnYet := false if cluster != "" { kubeIconHasBeenDrawnYet = true - p.appendSegment("kube-cluster", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "kube-cluster", Content: fmt.Sprintf("⎈ %s", cluster), Foreground: p.theme.KubeClusterFg, Background: p.theme.KubeClusterBg, @@ -113,10 +114,12 @@ func segmentKube(p *powerline) { if !kubeIconHasBeenDrawnYet { content = fmt.Sprintf("⎈ %s", content) } - p.appendSegment("kube-namespace", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "kube-namespace", Content: content, Foreground: p.theme.KubeNamespaceFg, Background: p.theme.KubeNamespaceBg, }) } + return segments } diff --git a/segment-load.go b/segment-load.go index 6797461c..4815c520 100644 --- a/segment-load.go +++ b/segment-load.go @@ -8,11 +8,11 @@ import ( "github.com/shirou/gopsutil/load" ) -func segmentLoad(p *powerline) { +func segmentLoad(p *powerline) []pwl.Segment { c := runtime.NumCPU() a, err := load.Avg() if err != nil { - return + return []pwl.Segment{} } bg := p.theme.LoadBg @@ -28,9 +28,10 @@ func segmentLoad(p *powerline) { bg = p.theme.LoadHighBg } - p.appendSegment("load", pwl.Segment{ + return []pwl.Segment{{ + Name: "load", Content: fmt.Sprintf("%.2f", a.Load5), Foreground: p.theme.LoadFg, Background: bg, - }) + }} } diff --git a/segment-newline.go b/segment-newline.go index 83f03268..69e7c9df 100644 --- a/segment-newline.go +++ b/segment-newline.go @@ -1,5 +1,7 @@ package main -func segmentNewline(p *powerline) { - p.newRow() +import pwl "github.com/justjanne/powerline-go/powerline" + +func segmentNewline(p *powerline) []pwl.Segment { + return []pwl.Segment{{NewLine: true}} } diff --git a/segment-nix-shell.go b/segment-nix-shell.go index 8359c4c3..00f8c459 100644 --- a/segment-nix-shell.go +++ b/segment-nix-shell.go @@ -5,15 +5,16 @@ import ( "os" ) -func segmentNixShell(p *powerline) { +func segmentNixShell(p *powerline) []pwl.Segment { var nixShell string nixShell, _ = os.LookupEnv("IN_NIX_SHELL") if nixShell == "" { - return + return []pwl.Segment{} } - p.appendSegment("nix-shell", pwl.Segment{ + return []pwl.Segment{{ + Name: "nix-shell", Content: nixShell, Foreground: p.theme.NixShellFg, Background: p.theme.NixShellBg, - }) + }} } diff --git a/segment-node.go b/segment-node.go index ec5bf475..898c2f94 100644 --- a/segment-node.go +++ b/segment-node.go @@ -14,7 +14,7 @@ type packageJSON struct { Version string `json:"version"` } -func segmentNode(p *powerline) { +func segmentNode(p *powerline) []pwl.Segment { stat, err := os.Stat(pkgfile) if err == nil && !stat.IsDir() { pkg := packageJSON{"!"} @@ -22,12 +22,14 @@ func segmentNode(p *powerline) { if err == nil { err = json.Unmarshal(raw, &pkg) if err == nil { - p.appendSegment("node-version", pwl.Segment{ + return []pwl.Segment{{ + Name: "node-segment", Content: pkg.Version + " \u2B22", Foreground: p.theme.NodeFg, Background: p.theme.NodeBg, - }) + }} } } } + return []pwl.Segment{} } diff --git a/segment-perlbrew.go b/segment-perlbrew.go index a6c59926..ba0483ff 100644 --- a/segment-perlbrew.go +++ b/segment-perlbrew.go @@ -6,16 +6,17 @@ import ( "path" ) -func segmentPerlbrew(p *powerline) { +func segmentPerlbrew(p *powerline) []pwl.Segment { env, _ := os.LookupEnv("PERLBREW_PERL") if env == "" { - return + return []pwl.Segment{} } envName := path.Base(env) - p.appendSegment("perlbrew", pwl.Segment{ + return []pwl.Segment{{ + Name: "perlbrew", Content: envName, Foreground: p.theme.PerlbrewFg, Background: p.theme.PerlbrewBg, - }) + }} } diff --git a/segment-plenv.go b/segment-plenv.go index 2ed3524f..f87d6956 100644 --- a/segment-plenv.go +++ b/segment-plenv.go @@ -6,19 +6,19 @@ import ( "path" ) -func segmentPlEnv(p *powerline) { +func segmentPlEnv(p *powerline) []pwl.Segment { var env string if env == "" { env, _ = os.LookupEnv("PLENV_VERSION") } - if env == "" { - return - } else { + if env != "" { envName := path.Base(env) - p.appendSegment("plenv", pwl.Segment{ + return []pwl.Segment{{ + Name: "plenv", Content: envName, Foreground: p.theme.PlEnvFg, Background: p.theme.PlEnvBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-plugin.go b/segment-plugin.go index f2ac8cb7..6208cd63 100644 --- a/segment-plugin.go +++ b/segment-plugin.go @@ -7,19 +7,16 @@ import ( pwl "github.com/justjanne/powerline-go/powerline" ) -func segmentPlugin(p *powerline, plugin string) bool { +func segmentPlugin(p *powerline, plugin string) ([]pwl.Segment, bool) { output, err := exec.Command("powerline-go-" + plugin).Output() if err != nil { - return false + return nil, false } segments := []pwl.Segment{} err = json.Unmarshal(output, &segments) if err != nil { // The plugin was found but no valid data was returned. Ignore it - return true + return []pwl.Segment{}, true } - for _, s := range segments { - p.appendSegment(plugin, s) - } - return true + return segments, true } diff --git a/segment-readonly.go b/segment-readonly.go index 9f48c370..ab3dc5bc 100644 --- a/segment-readonly.go +++ b/segment-readonly.go @@ -9,16 +9,18 @@ import ( "golang.org/x/sys/unix" ) -func segmentPerms(p *powerline) { +func segmentPerms(p *powerline) []pwl.Segment { cwd := p.cwd if cwd == "" { cwd, _ = os.LookupEnv("PWD") } if unix.Access(cwd, unix.W_OK) != nil { - p.appendSegment("perms", pwl.Segment{ + return []pwl.Segment{{ + Name: "perms", Content: p.symbolTemplates.Lock, Foreground: p.theme.ReadonlyFg, Background: p.theme.ReadonlyBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-readonly_windows.go b/segment-readonly_windows.go index 4dda8e28..11d34329 100644 --- a/segment-readonly_windows.go +++ b/segment-readonly_windows.go @@ -7,7 +7,7 @@ import ( "os" ) -func segmentPerms(p *powerline) { +func segmentPerms(p *powerline) []pwl.Segment { cwd := p.cwd if cwd == "" { cwd, _ = os.LookupEnv("PWD") @@ -17,10 +17,12 @@ func segmentPerms(p *powerline) { // Check user's permissions on directory in a portable but probably slower way fileInfo, _ := os.Stat(cwd) if fileInfo.Mode()&W_USR != W_USR { - p.appendSegment("perms", pwl.Segment{ + return []pwl.Segment{{ + Name: "perms", Content: p.symbolTemplates.Lock, Foreground: p.theme.ReadonlyFg, Background: p.theme.ReadonlyBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-root.go b/segment-root.go index e1a4df4e..4d5f0121 100644 --- a/segment-root.go +++ b/segment-root.go @@ -2,7 +2,7 @@ package main import pwl "github.com/justjanne/powerline-go/powerline" -func segmentRoot(p *powerline) { +func segmentRoot(p *powerline) []pwl.Segment { var foreground, background uint8 if *p.args.PrevError == 0 { foreground = p.theme.CmdPassedFg @@ -12,9 +12,10 @@ func segmentRoot(p *powerline) { background = p.theme.CmdFailedBg } - p.appendSegment("root", pwl.Segment{ + return []pwl.Segment{{ + Name: "root", Content: p.shellInfo.rootIndicator, Foreground: foreground, Background: background, - }) + }} } diff --git a/segment-shellvar.go b/segment-shellvar.go index 7afb1a48..ba832260 100644 --- a/segment-shellvar.go +++ b/segment-shellvar.go @@ -5,21 +5,23 @@ import ( "os" ) -func segmentShellVar(p *powerline) { +func segmentShellVar(p *powerline) []pwl.Segment { shellVarName := *p.args.ShellVar varContent, varExists := os.LookupEnv(shellVarName) if varExists { if varContent != "" { - p.appendSegment("shell-var", pwl.Segment{ + return []pwl.Segment{{ + Name: "shell-var", Content: varContent, Foreground: p.theme.ShellVarFg, Background: p.theme.ShellVarBg, - }) - } else { - warn("Shell variable " + shellVarName + " is empty.") + }} } + warn("Shell variable " + shellVarName + " is empty.") + } else { warn("Shell variable " + shellVarName + " does not exist.") } + return []pwl.Segment{} } diff --git a/segment-ssh.go b/segment-ssh.go index 4a57b6df..90c70ec4 100644 --- a/segment-ssh.go +++ b/segment-ssh.go @@ -5,13 +5,15 @@ import ( "os" ) -func segmentSSH(p *powerline) { +func segmentSSH(p *powerline) []pwl.Segment { sshClient, _ := os.LookupEnv("SSH_CLIENT") if sshClient != "" { - p.appendSegment("ssh", pwl.Segment{ + return []pwl.Segment{{ + Name: "ssh", Content: p.symbolTemplates.Network, Foreground: p.theme.SSHFg, Background: p.theme.SSHBg, - }) + }} } + return []pwl.Segment{} } diff --git a/segment-subversion.go b/segment-subversion.go index d2d17b34..7a2e7cb7 100644 --- a/segment-subversion.go +++ b/segment-subversion.go @@ -10,24 +10,27 @@ import ( var otherModified int -func addSvnRepoStatsSegment(p *powerline, nChanges int, symbol string, foreground uint8, background uint8) { +func addSvnRepoStatsSegment(p *powerline, nChanges int, symbol string, foreground uint8, background uint8) (segments []pwl.Segment) { if nChanges > 0 { - p.appendSegment("svn-status", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "svn-status", Content: fmt.Sprintf("%d%s", nChanges, symbol), Foreground: foreground, Background: background, }) } + return segments } -func (r repoStats) addSvnToPowerline(p *powerline) { - addSvnRepoStatsSegment(p, r.ahead, p.symbolTemplates.RepoAhead, p.theme.GitAheadFg, p.theme.GitAheadBg) - addSvnRepoStatsSegment(p, r.behind, p.symbolTemplates.RepoBehind, p.theme.GitBehindFg, p.theme.GitBehindBg) - addSvnRepoStatsSegment(p, r.staged, p.symbolTemplates.RepoStaged, p.theme.GitStagedFg, p.theme.GitStagedBg) - addSvnRepoStatsSegment(p, r.notStaged, p.symbolTemplates.RepoNotStaged, p.theme.GitNotStagedFg, p.theme.GitNotStagedBg) - addSvnRepoStatsSegment(p, r.untracked, p.symbolTemplates.RepoUntracked, p.theme.GitUntrackedFg, p.theme.GitUntrackedBg) - addSvnRepoStatsSegment(p, r.conflicted, p.symbolTemplates.RepoConflicted, p.theme.GitConflictedFg, p.theme.GitConflictedBg) - addSvnRepoStatsSegment(p, r.stashed, p.symbolTemplates.RepoStashed, p.theme.GitStashedFg, p.theme.GitStashedBg) +func (r repoStats) SvnSegments(p *powerline) (segments []pwl.Segment) { + segments = append(segments, addSvnRepoStatsSegment(p, r.ahead, p.symbolTemplates.RepoAhead, p.theme.GitAheadFg, p.theme.GitAheadBg)...) + segments = append(segments, addSvnRepoStatsSegment(p, r.behind, p.symbolTemplates.RepoBehind, p.theme.GitBehindFg, p.theme.GitBehindBg)...) + segments = append(segments, addSvnRepoStatsSegment(p, r.staged, p.symbolTemplates.RepoStaged, p.theme.GitStagedFg, p.theme.GitStagedBg)...) + segments = append(segments, addSvnRepoStatsSegment(p, r.notStaged, p.symbolTemplates.RepoNotStaged, p.theme.GitNotStagedFg, p.theme.GitNotStagedBg)...) + segments = append(segments, addSvnRepoStatsSegment(p, r.untracked, p.symbolTemplates.RepoUntracked, p.theme.GitUntrackedFg, p.theme.GitUntrackedBg)...) + segments = append(segments, addSvnRepoStatsSegment(p, r.conflicted, p.symbolTemplates.RepoConflicted, p.theme.GitConflictedFg, p.theme.GitConflictedBg)...) + segments = append(segments, addSvnRepoStatsSegment(p, r.stashed, p.symbolTemplates.RepoStashed, p.theme.GitStashedFg, p.theme.GitStashedBg)...) + return segments } func runSvnCommand(cmd string, args ...string) (string, error) { @@ -112,16 +115,16 @@ func parseSvnStatus() repoStats { return stats } -func segmentSubversion(p *powerline) { +func segmentSubversion(p *powerline) []pwl.Segment { svnInfo, err := parseSvnURL() if err != nil { - return + return []pwl.Segment{} } if len(p.ignoreRepos) > 0 { if p.ignoreRepos[svnInfo["URL"]] || p.ignoreRepos[svnInfo["Relative URL"]] { - return + return []pwl.Segment{} } } @@ -136,11 +139,13 @@ func segmentSubversion(p *powerline) { background = p.theme.RepoCleanBg } - p.appendSegment("svn-branch", pwl.Segment{ + segments := []pwl.Segment{{ + Name: "svn-branch", Content: svnInfo["Relative URL"], Foreground: foreground, Background: background, - }) + }} - svnStats.addSvnToPowerline(p) + segments = append(segments, svnStats.SvnSegments(p)...) + return segments } diff --git a/segment-termtitle.go b/segment-termtitle.go index e0c1c165..a3b2ec8d 100644 --- a/segment-termtitle.go +++ b/segment-termtitle.go @@ -10,12 +10,12 @@ import ( "strings" ) -func segmentTermTitle(p *powerline) { +func segmentTermTitle(p *powerline) []pwl.Segment { var title string term := os.Getenv("TERM") if !(strings.Contains(term, "xterm") || strings.Contains(term, "rxvt")) { - return + return []pwl.Segment{} } if *p.args.Shell == "bash" { @@ -29,9 +29,10 @@ func segmentTermTitle(p *powerline) { title = fmt.Sprintf("\033]0;%s@%s: %s\007", user, host, cwd) } - p.appendSegment("termtitle", pwl.Segment{ + return []pwl.Segment{{ + Name: "termtitle", Content: title, Priority: MaxInteger, // do not truncate HideSeparators: true, // do not draw separators - }) + }} } diff --git a/segment-terraform_workspace.go b/segment-terraform_workspace.go index 46d5047a..35c41edb 100644 --- a/segment-terraform_workspace.go +++ b/segment-terraform_workspace.go @@ -8,19 +8,20 @@ import ( const wsFile = "./.terraform/environment" -func segmentTerraformWorkspace(p *powerline) { +func segmentTerraformWorkspace(p *powerline) []pwl.Segment { stat, err := os.Stat(wsFile) if err == nil && !stat.IsDir() { workspace, err := ioutil.ReadFile(wsFile) if err == nil { - p.appendSegment("terraform-workspace", pwl.Segment{ + return []pwl.Segment{{ + Name: "terraform-workspace", Content: string(workspace), Foreground: p.theme.TFWsFg, Background: p.theme.TFWsBg, - }) + }} } } - + return []pwl.Segment{} } diff --git a/segment-time.go b/segment-time.go index 0488f6b9..e5c49730 100644 --- a/segment-time.go +++ b/segment-time.go @@ -5,10 +5,11 @@ import ( "time" ) -func segmentTime(p *powerline) { - p.appendSegment("time", pwl.Segment{ +func segmentTime(p *powerline) []pwl.Segment { + return []pwl.Segment{{ + Name: "time", Content: time.Now().Format("15:04:05"), Foreground: p.theme.TimeFg, Background: p.theme.TimeBg, - }) + }} } diff --git a/segment-username.go b/segment-username.go index ab51892c..3b2a0760 100644 --- a/segment-username.go +++ b/segment-username.go @@ -5,7 +5,7 @@ import ( "os" ) -func segmentUser(p *powerline) { +func segmentUser(p *powerline) []pwl.Segment { var userPrompt string if *p.args.Shell == "bash" { userPrompt = "\\u" @@ -23,9 +23,10 @@ func segmentUser(p *powerline) { background = p.theme.UsernameBg } - p.appendSegment("user", pwl.Segment{ + return []pwl.Segment{{ + Name: "user", Content: userPrompt, Foreground: p.theme.UsernameFg, Background: background, - }) + }} } diff --git a/segment-vgo.go b/segment-vgo.go index 99fb1f22..76ce3487 100644 --- a/segment-vgo.go +++ b/segment-vgo.go @@ -5,23 +5,27 @@ import ( "os" ) -func segmentVirtualGo(p *powerline) { +func segmentVirtualGo(p *powerline) []pwl.Segment { var env string if env == "" { env, _ = os.LookupEnv("VIRTUALGO") } + segments := []pwl.Segment{} if env == "" { - return + return segments } else { - p.appendSegment("vgo", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "vgo", Content: env, Foreground: p.theme.VirtualGoFg, Background: p.theme.VirtualGoBg, }) } - p.appendSegment("vgo", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "vgo", Content: env, Foreground: p.theme.VirtualGoFg, Background: p.theme.VirtualGoBg, }) + return segments } diff --git a/segment-virtualenv.go b/segment-virtualenv.go index e4f61309..4c5d143c 100644 --- a/segment-virtualenv.go +++ b/segment-virtualenv.go @@ -7,7 +7,7 @@ import ( pwl "github.com/justjanne/powerline-go/powerline" ) -func segmentVirtualEnv(p *powerline) { +func segmentVirtualEnv(p *powerline) []pwl.Segment { var env string if env == "" { env, _ = os.LookupEnv("VIRTUAL_ENV") @@ -18,20 +18,24 @@ func segmentVirtualEnv(p *powerline) { if env == "" { env, _ = os.LookupEnv("CONDA_DEFAULT_ENV") } + segments := []pwl.Segment{} if env == "" { - return + return segments } else { envName := path.Base(env) - p.appendSegment("venv", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "venv", Content: envName, Foreground: p.theme.VirtualEnvFg, Background: p.theme.VirtualEnvBg, }) } envName := path.Base(env) - p.appendSegment("venv", pwl.Segment{ + segments = append(segments, pwl.Segment{ + Name: "venv", Content: envName, Foreground: p.theme.VirtualEnvFg, Background: p.theme.VirtualEnvBg, }) + return segments }