Skip to content

Commit

Permalink
fix(ZVM): Inherit from language and fix color output
Browse files Browse the repository at this point in the history
Changed behavior to turn off colors for zvm when finding the version
  • Loading branch information
ggottemo committed Dec 14, 2024
1 parent b37e696 commit 581c062
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 54 deletions.
131 changes: 99 additions & 32 deletions src/segments/zvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,68 +7,135 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
)

const (
// DefaultZigIcon is the default icon used if none is specified
DefaultZigIcon = "ZVM"

// PropertyZigIcon is the property key for the zig icon
PropertyZigIcon properties.Property = "zigicon"
)

// Zvm represents a Zig Version Manager segment
type Zvm struct {
props properties.Properties
env runtime.Environment
language
Version string // Public for template access
ZigIcon string // Public for template access
colorCmd *colorCommand
}

type colorCommand struct {
env runtime.Environment
}

// colorState represents the ZVM color configuration state
type colorState struct {
enabled bool
valid bool
}

func (c *colorCommand) detectColorState() colorState {
output, err := c.env.RunCommand("zvm", "--color")
if err != nil {
return colorState{valid: false}
}

output = strings.ToLower(strings.TrimSpace(output))
switch output {
case "on", "yes", "y", "enabled", "true":
return colorState{enabled: true, valid: true}
case "off", "no", "n", "disabled", "false":
return colorState{enabled: false, valid: true}
default:
return colorState{valid: false}
}
}

Version string
ZigIcon string
func (c *colorCommand) setColor(enabled bool) error {
value := "false"
if enabled {
value = "true"
}
_, err := c.env.RunCommand("zvm", "--color", value)
return err
}

// SetText sets the version text
func (z *Zvm) SetText(text string) {
z.Version = text
}

// Text returns the current version
func (z *Zvm) Text() string {
return z.Version
}

const (
ZvmIcon properties.Property = "zigicon"
)

func (z *Zvm) Enabled() bool {
if !z.env.HasCommand("zvm") {
return false
}
version := z.getZvmVersion()
return version != ""
}

// Template returns the template string for the segment
func (z *Zvm) Template() string {
return " {{ if .ZigIcon }}{{ .ZigIcon }} {{ end }}{{ .Version }} "
}

// Init initializes the segment with the given properties and environment
func (z *Zvm) Init(props properties.Properties, env runtime.Environment) {
z.props = props
z.env = env
z.colorCmd = &colorCommand{env: env}

// Initialize the icon from properties
z.ZigIcon = z.props.GetString(ZvmIcon, "ZVM")
z.Version = z.getZvmVersion()
z.ZigIcon = z.props.GetString(PropertyZigIcon, DefaultZigIcon)

// Only try to get version if zvm command exists
if z.env.HasCommand("zvm") {
z.Version = z.getZvmVersion()
}
}

// Enabled returns true if the segment should be enabled
func (z *Zvm) Enabled() bool {
if !z.env.HasCommand("zvm") {
return false
}
return z.Version != ""
}

// getZvmVersion returns the current active Zvm version
func (z *Zvm) getZvmVersion() string {
// Detect current color state
originalState := z.colorCmd.detectColorState()

// If we couldn't detect the state, proceed with color disabled
if !originalState.valid {
if err := z.colorCmd.setColor(false); err != nil {
return ""
}
defer func() {
_ = z.colorCmd.setColor(true) // Best effort to restore color
}()
} else if originalState.enabled {
// Temporarily disable colors if they were enabled
if err := z.colorCmd.setColor(false); err != nil {
return ""
}
defer func() {
_ = z.colorCmd.setColor(originalState.enabled) // Restore original state
}()
}

// Get version list
output, err := z.env.RunCommand("zvm", "list")
if err != nil {
return ""
}

// Split output into lines
lines := strings.Split(output, "\n")

// Look for line containing green color code which indicates active version
// ANSI color code for green is typically \033[32m or \x1b[32m
for _, line := range lines {
if strings.Contains(line, "\x1b[32m") || strings.Contains(line, "\033[32m") {
// Clean ANSI color codes and whitespace
cleaned := strings.ReplaceAll(line, "\x1b[32m", "")
cleaned = strings.ReplaceAll(cleaned, "\033[32m", "")
cleaned = strings.ReplaceAll(cleaned, "\x1b[0m", "")
cleaned = strings.TrimSpace(cleaned)
return parseActiveVersion(output)
}

return cleaned
// parseActiveVersion extracts the active version from zvm list output
func parseActiveVersion(output string) string {
words := strings.Fields(output)
for _, word := range words {
if !strings.Contains(word, "[x]") {
continue
}
return strings.TrimSpace(strings.ReplaceAll(word, "[x]", ""))
}
return ""
}
116 changes: 95 additions & 21 deletions src/segments/zvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ package segments
import (
"testing"

"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"

"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
"github.com/stretchr/testify/assert"
)

Expand All @@ -14,52 +13,77 @@ func TestZvm(t *testing.T) {
Case string
ExpectedString string
HasCommand bool
ColorState string
ListOutput string
Properties properties.Map
Template string
ExpectedIcon string
}{
{
Case: "no zvm command",
ExpectedString: "",
HasCommand: false,
ColorState: "",
ListOutput: "",
Properties: properties.Map{},
Template: " {{ if .ZigIcon }}{{ .ZigIcon }} {{ end }}{{ .Version }} ",
ExpectedIcon: DefaultZigIcon,
},
{
Case: "version 0.13.0 active",
Case: "version with colors enabled",
ExpectedString: "0.13.0",
HasCommand: true,
ListOutput: "0.11.0\n\x1b[32m0.13.0\x1b[0m\n0.12.0",
ColorState: "on",
ListOutput: "0.11.0\n[x]0.13.0\n0.12.0",
Properties: properties.Map{},
Template: " {{ if .ZigIcon }}{{ .ZigIcon }} {{ end }}{{ .Version }} ",
ExpectedIcon: DefaultZigIcon,
},
{
Case: "version 0.13.0 active with icon",
Case: "version with colors disabled",
ExpectedString: "0.13.0",
HasCommand: true,
ListOutput: "0.11.0\n\x1b[32m0.13.0\x1b[0m\n0.12.0",
Properties: properties.Map{
ZvmIcon: "⚡",
},
Template: " {{ if .ZigIcon }}{{ .ZigIcon }} {{ end }}{{ .Version }} ",
ColorState: "off",
ListOutput: "0.11.0\n[x]0.13.0\n0.12.0",
Properties: properties.Map{},
Template: " {{ if .ZigIcon }}{{ .ZigIcon }} {{ end }}{{ .Version }} ",
ExpectedIcon: DefaultZigIcon,
},
{
Case: "version 0.12.0-dev active",
ExpectedString: "0.12.0-dev.1234+abcdef",
Case: "version with custom icon",
ExpectedString: "0.13.0",
HasCommand: true,
ListOutput: "0.11.0\n\x1b[32m0.12.0-dev.1234+abcdef\x1b[0m\n0.12.0",
Properties: properties.Map{},
// Change all test cases to expect the actual template
Template: " {{ if .ZigIcon }}{{ .ZigIcon }} {{ end }}{{ .Version }} ",
ColorState: "on",
ListOutput: "0.11.0\n[x]0.13.0\n0.12.0",
Properties: properties.Map{
PropertyZigIcon: "⚡",
},
Template: " {{ if .ZigIcon }}{{ .ZigIcon }} {{ end }}{{ .Version }} ",
ExpectedIcon: "⚡",
},
}

for _, tc := range cases {
t.Run(tc.Case, func(t *testing.T) {
env := new(mock.Environment)

// Mock HasCommand first
env.On("HasCommand", "zvm").Return(tc.HasCommand)
env.On("RunCommand", "zvm", []string{"list"}).Return(tc.ListOutput, nil)

// Only set up other mocks if HasCommand is true
if tc.HasCommand {
// Mock color detection
env.On("RunCommand", "zvm", []string{"--color"}).Return(tc.ColorState, nil)

// Mock color state changes based on detected state
if tc.ColorState == "on" {
env.On("RunCommand", "zvm", []string{"--color", "false"}).Return("", nil)
env.On("RunCommand", "zvm", []string{"--color", "true"}).Return("", nil)
}

// Mock version list command
env.On("RunCommand", "zvm", []string{"list"}).Return(tc.ListOutput, nil)
}

zvm := &Zvm{}
zvm.Init(tc.Properties, env)
Expand All @@ -68,13 +92,63 @@ func TestZvm(t *testing.T) {

if tc.HasCommand {
assert.True(t, zvm.Enabled())
assert.Equal(t, tc.ExpectedString, zvm.Version)
if icon, ok := tc.Properties[ZvmIcon]; ok {
assert.Equal(t, icon, zvm.ZigIcon)
}
assert.Equal(t, tc.ExpectedString, zvm.Text())
assert.Equal(t, tc.ExpectedIcon, zvm.ZigIcon)
} else {
assert.False(t, zvm.Enabled())
assert.Empty(t, zvm.Text())
}

// Verify all expected calls were made
env.AssertExpectations(t)
})
}
}

func TestColorStateDetection(t *testing.T) {
cases := []struct {
Case string
ColorOutput string
Expected colorState
}{
{
Case: "enabled - on",
ColorOutput: "on",
Expected: colorState{enabled: true, valid: true},
},
{
Case: "enabled - yes",
ColorOutput: "yes",
Expected: colorState{enabled: true, valid: true},
},
{
Case: "disabled - off",
ColorOutput: "off",
Expected: colorState{enabled: false, valid: true},
},
{
Case: "disabled - no",
ColorOutput: "no",
Expected: colorState{enabled: false, valid: true},
},
{
Case: "invalid state",
ColorOutput: "invalid",
Expected: colorState{enabled: false, valid: false},
},
}

for _, tc := range cases {
t.Run(tc.Case, func(t *testing.T) {
env := new(mock.Environment)
env.On("RunCommand", "zvm", []string{"--color"}).Return(tc.ColorOutput, nil)

cmd := &colorCommand{env: env}
state := cmd.detectColorState()

assert.Equal(t, tc.Expected.enabled, state.enabled)
assert.Equal(t, tc.Expected.valid, state.valid)
env.AssertExpectations(t)
})
}
}
2 changes: 1 addition & 1 deletion themes/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4933,7 +4933,7 @@
"type": "string",
"title": "Zig Icon",
"description": "icon to display before the version",
"default": "ZVM - "
"default": "ZVM"
}
}
}
Expand Down

0 comments on commit 581c062

Please sign in to comment.