Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix/perf: don't call language detection command in enabled() #285

Merged
merged 1 commit into from
Dec 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/docs/segment-dotnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ Display the currently active .NET SDK version.

- display_version: `boolean` - display the active version or not; useful if all you need is an icon indicating `dotnet`
is present - defaults to `true`
- missing_command_text: `string` - text to display when the command is missing - default to ``
- unsupported_version_icon: `string` - text/icon that is displayed when the active .NET SDK version (e.g., one specified
by `global.json`) is not installed/supported - defaults to `\u2327` (X in a rectangle box)
by `global.json`) is not installed/supported - defaults to `\uf071` (X in a rectangle box)
1 change: 1 addition & 0 deletions docs/docs/segment-golang.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Display the currently active golang version when a folder contains `.go` files.
## Properties

- display_version: `boolean` - display the golang version - defaults to `true`
- missing_command_text: `string` - text to display when the command is missing - default to ``
- display_mode: `string` - determines when the segment is displayed
- `always`: The segment is always displayed
- `context`: The segment is only displayed when *.go or go.mod files are present (default)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/segment-julia.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Display the currently active julia version when a folder contains `.jl` files.
## Properties

- display_version: `boolean` - display the julia version - defaults to `true`
- missing_command_text: `string` - text to display when the command is missing - default to ``
- display_mode: `string` - determines when the segment is displayed
- `always`: The segment is always displayed
- `context`: The segment is only displayed when *.jl files are present (default)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/segment-node.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Display the currently active node version when a folder contains `.js` or `.ts`
## Properties

- display_version: `boolean` - display the node version - defaults to `true`
- missing_command_text: `string` - text to display when the command is missing - default to ``
- display_mode: `string` - determines when the segment is displayed
- `always`: The segment is always displayed
- `context`: The segment is only displayed when *.js, *.ts or package.json files are present (default)
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/segment-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Supports conda, virtualenv and pyenv.
## Properties

- display_virtual_env: `boolean` - show the name of the virtualenv or not - defaults to `true`
- display_version: `boolean` - display the python version - defaults to `true`
- missing_command_text: `string` - text to display when the command is missing - default to ``
- display_mode: `string` - determines when the segment is displayed
- `always`: The segment is always displayed
- `context`: The segment is only displayed when *.py or *.ipynb files are present (default)
Expand Down
8 changes: 4 additions & 4 deletions src/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type environmentInfo interface {
getHostName() (string, error)
getRuntimeGOOS() string
getPlatform() string
hasCommand(command string) bool
hasCommand(command string) (string, bool)
runCommand(command string, args ...string) (string, error)
runShellCommand(shell, command string) string
lastErrorCode() int
Expand Down Expand Up @@ -221,9 +221,9 @@ func (env *environment) runShellCommand(shell, command string) string {
return strings.TrimSpace(string(out))
}

func (env *environment) hasCommand(command string) bool {
_, err := exec.LookPath(command)
return err == nil
func (env *environment) hasCommand(command string) (string, bool) {
path, err := exec.LookPath(command)
return path, err == nil
}

func (env *environment) lastErrorCode() int {
Expand Down
5 changes: 3 additions & 2 deletions src/segment_az.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ func (a *az) init(props *properties, env environmentInfo) {
}

func (a *az) enabled() bool {
if (!a.idEnabled() && !a.nameEnabled()) || !a.env.hasCommand("az") {
commandPath, commandExists := a.env.hasCommand("az")
if (!a.idEnabled() && !a.nameEnabled()) || !commandExists {
return false
}

output, _ := a.env.runCommand("az", "account", "show", "--query=[name,id]", "-o=tsv")
output, _ := a.env.runCommand(commandPath, "account", "show", "--query=[name,id]", "-o=tsv")
if output == "" {
return false
}
Expand Down
2 changes: 1 addition & 1 deletion src/segment_az_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type azArgs struct {

func bootStrapAzTest(args *azArgs) *az {
env := new(MockedEnvironment)
env.On("hasCommand", "az").Return(args.enabled)
env.On("hasCommand", "az").Return("az", args.enabled)
env.On("runCommand", "az", []string{"account", "show", "--query=[name,id]", "-o=tsv"}).Return(fmt.Sprintf("%s\n%s\n", args.subscriptionName, args.subscriptionID), nil)
props := &properties{
values: map[Property]interface{}{
Expand Down
3 changes: 2 additions & 1 deletion src/segment_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ const (

func (c *command) enabled() bool {
shell := c.props.getString(ExecutableShell, "bash")
if !c.env.hasCommand(shell) {
shell, commandExists := c.env.hasCommand(shell)
if !commandExists {
return false
}
command := c.props.getString(Command, "echo no command specified")
Expand Down
53 changes: 17 additions & 36 deletions src/segment_dotnet.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
package main

import (
"errors"
)

type dotnet struct {
props *properties
env environmentInfo
activeVersion string
unsupportedVersion bool
language *language
}

const (
Expand All @@ -18,41 +11,29 @@ const (
)

func (d *dotnet) string() string {
if d.unsupportedVersion {
return d.props.getString(UnsupportedDotnetVersionIcon, "\u2327")
}
version := d.language.string()

if d.props.getBool(DisplayVersion, true) {
return d.activeVersion
// Exit code 145 is a special indicator that dotnet
// ran, but the current project config settings specify
// use of an SDK that isn't installed.
if d.language.exitCode == 145 {
return d.language.props.getString(UnsupportedDotnetVersionIcon, "\uf071 ")
}

return ""
return version
}

func (d *dotnet) init(props *properties, env environmentInfo) {
d.props = props
d.env = env
d.language = &language{
env: env,
props: props,
commands: []string{"dotnet"},
versionParam: "--version",
extensions: []string{"*.cs", "*.vb", "*.sln", "*.csproj", "*.vbproj"},
versionRegex: `(?P<version>[0-9]+.[0-9]+.[0-9]+)`,
}
}

func (d *dotnet) enabled() bool {
if !d.env.hasCommand("dotnet") {
return false
}

output, err := d.env.runCommand("dotnet", "--version")
if err == nil {
d.activeVersion = output
return true
}

// Exit code 145 is a special indicator that dotnet
// ran, but the current project config settings specify
// use of an SDK that isn't installed.
var exerr *commandError
if errors.As(err, &exerr) && exerr.exitCode == 145 {
d.unsupportedVersion = true
return true
}

return false
return d.language.enabled()
}
15 changes: 8 additions & 7 deletions src/segment_dotnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,33 @@ type dotnetArgs struct {

func bootStrapDotnetTest(args *dotnetArgs) *dotnet {
env := new(MockedEnvironment)
env.On("hasCommand", "dotnet").Return(args.enabled)
env.On("hasCommand", "dotnet").Return("dotnet", args.enabled)
if args.unsupported {
err := &commandError{exitCode: 145}
env.On("runCommand", "dotnet", []string{"--version"}).Return("", err)
} else {
env.On("runCommand", "dotnet", []string{"--version"}).Return(args.version, nil)
}

env.On("hasFiles", "*.cs").Return(true)
env.On("getPathSeperator", nil).Return("")
props := &properties{
values: map[Property]interface{}{
DisplayVersion: args.displayVersion,
UnsupportedDotnetVersionIcon: args.unsupportedIcon,
},
}
a := &dotnet{
env: env,
props: props,
}
return a
dotnet := &dotnet{}
dotnet.init(props, env)
return dotnet
}

func TestEnabledDotnetNotFound(t *testing.T) {
args := &dotnetArgs{
enabled: false,
}
dotnet := bootStrapDotnetTest(args)
assert.False(t, dotnet.enabled())
assert.True(t, dotnet.enabled())
}

func TestDotnetVersionNotDisplayed(t *testing.T) {
Expand Down
15 changes: 9 additions & 6 deletions src/segment_git.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ func (s *gitStatus) string(prefix, color string) string {
}

type git struct {
props *properties
env environmentInfo
repo *gitRepo
props *properties
env environmentInfo
repo *gitRepo
commandPath string
}

const (
Expand Down Expand Up @@ -114,10 +115,12 @@ const (
)

func (g *git) enabled() bool {
if !g.env.hasCommand("git") {
commandPath, commandExists := g.env.hasCommand("git")
if !commandExists {
return false
}
output, _ := g.env.runCommand("git", "rev-parse", "--is-inside-work-tree")
g.commandPath = commandPath
output, _ := g.env.runCommand(g.commandPath, "rev-parse", "--is-inside-work-tree")
return output == "true"
}

Expand Down Expand Up @@ -235,7 +238,7 @@ func (g *git) getStatusColor(defaultValue string) string {

func (g *git) getGitCommandOutput(args ...string) string {
args = append([]string{"-c", "core.quotepath=false", "-c", "color.status=false"}, args...)
val, _ := g.env.runCommand("git", args...)
val, _ := g.env.runCommand(g.commandPath, args...)
return val
}

Expand Down
17 changes: 11 additions & 6 deletions src/segment_git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const (

func TestEnabledGitNotFound(t *testing.T) {
env := new(MockedEnvironment)
env.On("hasCommand", "git").Return(false)
env.On("hasCommand", "git").Return("", false)
g := &git{
env: env,
}
Expand All @@ -21,7 +21,7 @@ func TestEnabledGitNotFound(t *testing.T) {

func TestEnabledInWorkingDirectory(t *testing.T) {
env := new(MockedEnvironment)
env.On("hasCommand", "git").Return(true)
env.On("hasCommand", "git").Return("git", true)
env.On("runCommand", "git", []string{"rev-parse", "--is-inside-work-tree"}).Return("true", nil)
g := &git{
env: env,
Expand All @@ -36,7 +36,8 @@ func TestGetGitOutputForCommand(t *testing.T) {
env := new(MockedEnvironment)
env.On("runCommand", "git", append(args, commandArgs...)).Return(want, nil)
g := &git{
env: env,
env: env,
commandPath: "git",
}
got := g.getGitCommandOutput(commandArgs...)
assert.Equal(t, want, got)
Expand Down Expand Up @@ -85,6 +86,7 @@ func setupHEADContextEnv(context *detachedContext) *git {
repo: &gitRepo{
root: "",
},
commandPath: "git",
}
return g
}
Expand Down Expand Up @@ -211,7 +213,8 @@ func TestGetStashContextZeroEntries(t *testing.T) {
env := new(MockedEnvironment)
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "rev-list", "--walk-reflogs", "--count", "refs/stash"}).Return("", nil)
g := &git{
env: env,
env: env,
commandPath: "git",
}
got := g.getStashContext()
assert.Equal(t, want, got)
Expand All @@ -222,7 +225,8 @@ func TestGetStashContextMultipleEntries(t *testing.T) {
env := new(MockedEnvironment)
env.On("runCommand", "git", []string{"-c", "core.quotepath=false", "-c", "color.status=false", "rev-list", "--walk-reflogs", "--count", "refs/stash"}).Return("2", nil)
g := &git{
env: env,
env: env,
commandPath: "git",
}
got := g.getStashContext()
assert.Equal(t, want, got)
Expand Down Expand Up @@ -390,7 +394,8 @@ func bootstrapUpstreamTest(upstream string) *git {
repo: &gitRepo{
upstream: "origin/main",
},
props: props,
props: props,
commandPath: "git",
}
return g
}
Expand Down
5 changes: 3 additions & 2 deletions src/segment_kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ func (k *kubectl) init(props *properties, env environmentInfo) {
}

func (k *kubectl) enabled() bool {
if !k.env.hasCommand("kubectl") {
commandPath, commandExists := k.env.hasCommand("kubectl")
if !commandExists {
return false
}
k.contextName, _ = k.env.runCommand("kubectl", "config", "current-context")
k.contextName, _ = k.env.runCommand(commandPath, "config", "current-context")
return k.contextName != ""
}
2 changes: 1 addition & 1 deletion src/segment_kubectl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type kubectlArgs struct {

func bootStrapKubectlTest(args *kubectlArgs) *kubectl {
env := new(MockedEnvironment)
env.On("hasCommand", "kubectl").Return(args.enabled)
env.On("hasCommand", "kubectl").Return("kubectl", args.enabled)
env.On("runCommand", "kubectl", []string{"config", "current-context"}).Return(args.contextName, nil)
k := &kubectl{
env: env,
Expand Down
Loading