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

feat(ZVM): Add ZVM segment and docs #5990

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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: 3 additions & 0 deletions src/config/segment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ const (
YTM SegmentType = "ytm"
// ZIG writes the active zig version
ZIG SegmentType = "zig"
// ZVM writes the active zig version used in the zvm environment
ZVM SegmentType = "zvm"
)

// Segments contains all available prompt segment writers.
Expand Down Expand Up @@ -331,6 +333,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
YARN: func() SegmentWriter { return &segments.Yarn{} },
YTM: func() SegmentWriter { return &segments.Ytm{} },
ZIG: func() SegmentWriter { return &segments.Zig{} },
ZVM: func() SegmentWriter { return &segments.Zvm{} },
}

func (segment *Segment) MapSegmentWithWriter(env runtime.Environment) error {
Expand Down
141 changes: 141 additions & 0 deletions src/segments/zvm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package segments

import (
"strings"

"github.com/jandedobbeleer/oh-my-posh/src/properties"
"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 {
ggottemo marked this conversation as resolved.
Show resolved Hide resolved
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 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this necessary? That switch should not reset the entire command output for following commands, right? If so, this is terrible design on their part.

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}
}
}

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
}

// 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}

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")
ggottemo marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return ""
}

return parseActiveVersion(output)
}

// 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 ""
}
154 changes: 154 additions & 0 deletions src/segments/zvm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package segments

import (
"testing"

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

func TestZvm(t *testing.T) {
cases := []struct {
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 with colors enabled",
ExpectedString: "0.13.0",
HasCommand: true,
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 with colors disabled",
ExpectedString: "0.13.0",
HasCommand: true,
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 with custom icon",
ExpectedString: "0.13.0",
HasCommand: true,
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)

// 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)

assert.Equal(t, tc.Template, zvm.Template())

if tc.HasCommand {
assert.True(t, zvm.Enabled())
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)
})
}
}
26 changes: 25 additions & 1 deletion themes/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,8 @@
"xmake",
"yarn",
"ytm",
"zig"
"zig",
"zvm"
]
},
"style": {
Expand Down Expand Up @@ -4916,6 +4917,29 @@
}
}
},
{
"if": {
"properties": {
"type": { "const": "zvm" }
}
},
"then": {
"title": "ZVM Segment",
"description": "https://ohmyposh.dev/docs/zvm",
"properties": {
"properties": {
"properties": {
"zigicon": {
"type": "string",
"title": "Zig Icon",
"description": "icon to display before the version",
"default": "ZVM"
}
}
}
}
}
},
{
"if": {
"properties": {
Expand Down
35 changes: 35 additions & 0 deletions website/docs/segments/cli/zvm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
id: zvm
title: ZVM
sidebar_label: ZVM
---

## What

Display the current Zig version being used by zvm (Zig Version Manager).

## Sample Configuration


import Config from '@site/src/components/Config.js';


<Config data={{
"type": "zvm",
"style": "powerline",
"powerline_symbol": "\uE0B0",
"foreground": "#F7A41D",
"background": "#193549",
"properties": {
"zigicon": "ZVM - "
}
}}/>

## Properties

| Name | Type | Description |
| --------- | -------- | -------------------------------------- |
| `zigicon` | `string` | The icon to display before the version |


[ZVM](https://github.com/tristanisham/zvm)
1 change: 1 addition & 0 deletions website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ module.exports = {
"segments/cli/unity",
"segments/cli/xmake",
"segments/cli/yarn",
"segments/cli/zvm",
]
},
{
Expand Down