diff --git a/src/segments/path.go b/src/segments/path.go index ec2b035990d3..bfb161a5196b 100644 --- a/src/segments/path.go +++ b/src/segments/path.go @@ -3,6 +3,7 @@ package segments import ( "fmt" "sort" + "strconv" "strings" "github.com/jandedobbeleer/oh-my-posh/src/platform" @@ -190,13 +191,40 @@ func (pt *Path) setStyle() { case FolderType: pt.Path = pt.getFolderPath() case Powerlevel: - maxWidth := int(pt.props.GetFloat64(MaxWidth, 0)) + maxWidth := pt.getMaxWidth() pt.Path = pt.getUniqueLettersPath(maxWidth) default: pt.Path = fmt.Sprintf("Path style: %s is not available", style) } } +func (pt *Path) getMaxWidth() int { + width := pt.props.GetString(MaxWidth, "") + if len(width) == 0 { + return 0 + } + + tmpl := &template.Text{ + Template: width, + Context: pt, + Env: pt.env, + } + + text, err := tmpl.Render() + if err != nil { + pt.env.Error(err) + return 0 + } + + value, err := strconv.Atoi(text) + if err != nil { + pt.env.Error(err) + return 0 + } + + return value +} + func (pt *Path) getFolderSeparator() string { separatorTemplate := pt.props.GetString(FolderSeparatorTemplate, "") if len(separatorTemplate) == 0 { diff --git a/src/segments/path_test.go b/src/segments/path_test.go index 414d61a90d47..55971038b64a 100644 --- a/src/segments/path_test.go +++ b/src/segments/path_test.go @@ -744,6 +744,9 @@ func TestAgnosterPathStyles(t *testing.T) { } env.On("Flags").Return(args) env.On("Shell").Return(shell.PWSH) + + env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil) + path := &Path{ env: env, props: properties.Map{ @@ -754,6 +757,7 @@ func TestAgnosterPathStyles(t *testing.T) { HideRootLocation: tc.HideRootLocation, }, } + path.setPaths() path.setStyle() got := renderTemplateNoTrimSpace(env, "{{ .Path }}", path) @@ -1570,3 +1574,53 @@ func TestSplitPath(t *testing.T) { assert.Equal(t, tc.Expected, got, tc.Case) } } + +func TestGetMaxWidth(t *testing.T) { + cases := []struct { + Case string + MaxWidth any + Expected int + }{ + { + Case: "Nil", + Expected: 0, + }, + { + Case: "Empty string", + MaxWidth: "", + Expected: 0, + }, + { + Case: "Invalid template", + MaxWidth: "{{ .Unknown }}", + Expected: 0, + }, + { + Case: "Environment variable", + MaxWidth: "{{ .Env.MAX_WIDTH }}", + Expected: 120, + }, + } + + for _, tc := range cases { + env := new(mock.MockedEnvironment) + env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil) + env.On("Error", mock2.Anything).Return(nil) + env.On("TemplateCache").Return(&platform.TemplateCache{ + Env: map[string]string{ + "MAX_WIDTH": "120", + }, + Shell: "bash", + }) + + path := &Path{ + env: env, + props: properties.Map{ + MaxWidth: tc.MaxWidth, + }, + } + + got := path.getMaxWidth() + assert.Equal(t, tc.Expected, got, tc.Case) + } +} diff --git a/src/template/text.go b/src/template/text.go index 81630aeae5bc..51fdaa1685a6 100644 --- a/src/template/text.go +++ b/src/template/text.go @@ -70,19 +70,25 @@ func (c *Context) init(t *Text) { func (t *Text) Render() (string, error) { t.Env.DebugF("Rendering template: %s", t.Template) + if !strings.Contains(t.Template, "{{") || !strings.Contains(t.Template, "}}") { return t.Template, nil } + t.cleanTemplate() + tmpl, err := template.New(t.Template).Funcs(funcMap()).Parse(t.Template) if err != nil { t.Env.Error(err) return "", errors.New(InvalidTemplate) } + context := &Context{} context.init(t) + buffer := new(bytes.Buffer) defer buffer.Reset() + err = tmpl.Execute(buffer, context) if err != nil { t.Env.Error(err) @@ -92,10 +98,12 @@ func (t *Text) Render() (string, error) { } return "", errors.New(msg["MSG"]) } + text := buffer.String() // issue with missingkey=zero ignored for map[string]any // https://github.com/golang/go/issues/24963 text = strings.ReplaceAll(text, "", "") + return text, nil } @@ -103,19 +111,23 @@ func (t *Text) cleanTemplate() { isKnownVariable := func(variable string) bool { variable = strings.TrimPrefix(variable, ".") splitted := strings.Split(variable, ".") + if len(splitted) == 0 { return true } + variable = splitted[0] // check if alphanumeric if !regex.MatchString(`^[a-zA-Z0-9]+$`, variable) { return true } + for _, b := range knownVariables { if variable == b { return true } } + return false } diff --git a/themes/schema.json b/themes/schema.json index f313c643da58..6cf9ab4729ae 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -2479,7 +2479,7 @@ "default": 1 }, "max_width": { - "type": "integer", + "type": ["integer","string"], "title": "Maximum Width", "description": "Maximum path width to display for powerlevel style", "default": 0 diff --git a/website/docs/segments/path.mdx b/website/docs/segments/path.mdx index 92ef151538a0..2f29b2ba6e57 100644 --- a/website/docs/segments/path.mdx +++ b/website/docs/segments/path.mdx @@ -30,25 +30,25 @@ import Config from "@site/src/components/Config.js"; ## Properties -| Name | Type | Default | Description | +| Name | Type | Default | Description | | --------------------------- | :--------: | :--------: | ---------------------------------------------------------------------------------------------------------------- | -| `folder_separator_icon` | `string` | `/` | the symbol to use as a separator between folders | -| `folder_separator_template` | `string` | | the [template][templates] to use as a separator between folders | -| `home_icon` | `string` | `~` | the icon to display when at `$HOME` | -| `folder_icon` | `string` | `..` | the icon to use as a folder indication | -| `windows_registry_icon` | `string` | `\uF013` | the icon to display when in the Windows registry | -| `style` | `enum` | `agnoster` | how to display the current path | -| `mixed_threshold` | `number` | `4` | the maximum length of a path segment that will be displayed when using `Mixed` | -| `max_depth` | `number` | `1` | maximum path depth to display before shortening when using `agnoster_short` | -| `max_width` | `number` | `0` | maximum path length to display when using `powerlevel` | -| `hide_root_location` | `boolean` | `false` | ides the root location if it doesn't fit in the last `max_depth` folders, when using `agnoster_short` | +| `folder_separator_icon` | `string` | `/` | the symbol to use as a separator between folders | +| `folder_separator_template` | `string` | | the [template][templates] to use as a separator between folders | +| `home_icon` | `string` | `~` | the icon to display when at `$HOME` | +| `folder_icon` | `string` | `..` | the icon to use as a folder indication | +| `windows_registry_icon` | `string` | `\uF013` | the icon to display when in the Windows registry | +| `style` | `enum` | `agnoster` | how to display the current path | +| `mixed_threshold` | `number` | `4` | the maximum length of a path segment that will be displayed when using `Mixed` | +| `max_depth` | `number` | `1` | maximum path depth to display before shortening when using `agnoster_short` | +| `max_width` | `any` | `0` | maximum path length to display when using `powerlevel`, can leverage [templates] | +| `hide_root_location` | `boolean` | `false` | ides the root location if it doesn't fit in the last `max_depth` folders, when using `agnoster_short` | | `cycle` | `[]string` | | a list of color overrides to cycle through to colorize the individual path folders, e.g. `[ "#ffffff,#111111" ]` | -| `cycle_folder_separator` | `boolean` | `false` | colorize the `folder_separator_icon` as well when using a cycle | -| `folder_format` | `string` | `%s` | format to use on individual path folders | -| `edge_format` | `string` | `%s` | format to use on the first and last folder of the path | -| `left_format` | `string` | `%s` | format to use on the firstfolder of the path - defaults to `edge_format` | -| `right_format` | `string` | `%s` | format to use on the last folder of the path - defaults to `edge_format` | -| `gitdir_format` | `string` | | format to use for a git root directory | +| `cycle_folder_separator` | `boolean` | `false` | colorize the `folder_separator_icon` as well when using a cycle | +| `folder_format` | `string` | `%s` | format to use on individual path folders | +| `edge_format` | `string` | `%s` | format to use on the first and last folder of the path | +| `left_format` | `string` | `%s` | format to use on the firstfolder of the path - defaults to `edge_format` | +| `right_format` | `string` | `%s` | format to use on the last folder of the path - defaults to `edge_format` | +| `gitdir_format` | `string` | | format to use for a git root directory | ## Mapped Locations @@ -56,7 +56,7 @@ Allows you to override a location with an icon/string. It validates if the current path **starts with the specific elements** and replaces it with the icon/string if there's a match. To avoid issues with nested overrides, Oh My Posh will sort the list of mapped locations before doing a replacement. -| Name | Type | Default | Description | +| Name | Type | Default | Description | | -------------------------- | :-------: | :-----: | ---------------------------------------------------------------------------------------- | | `mapped_locations_enabled` | `boolean` | `true` | replace known locations in the path with the replacements before applying the style | | `mapped_locations` | `object` | | custom glyph/text for specific paths. Works regardless of the `mapped_locations_enabled` |