diff --git a/src/cache/duration.go b/src/cache/duration.go index 1163827c..f11768d7 100644 --- a/src/cache/duration.go +++ b/src/cache/duration.go @@ -1,7 +1,6 @@ package cache import ( - "strconv" "time" ) @@ -38,5 +37,6 @@ func ToDuration(seconds int) Duration { return INFINITE } - return Duration(strconv.Itoa(seconds) + "s") + duration := time.Duration(seconds) * time.Second + return Duration(duration.String()) } diff --git a/src/cache/file.go b/src/cache/file.go index e1e9d2a0..cf34678a 100644 --- a/src/cache/file.go +++ b/src/cache/file.go @@ -67,9 +67,11 @@ func (fc *File) Get(key string) (string, bool) { if !found { return "", false } + if co, ok := val.(*Entry); ok { return co.Value, true } + return "", false } diff --git a/src/config/migrate.go b/src/config/migrate.go index 18deffef..2adf50b3 100644 --- a/src/config/migrate.go +++ b/src/config/migrate.go @@ -2,6 +2,13 @@ package config import ( "github.com/jandedobbeleer/oh-my-posh/src/cache" + "github.com/jandedobbeleer/oh-my-posh/src/properties" +) + +const ( + includeFolders = properties.Property("include_folders") + excludeFolders = properties.Property("exclude_folders") + cacheTimeout = properties.Property("cache_timeout") ) func (cfg *Config) Migrate() { @@ -23,11 +30,46 @@ func (segment *Segment) migrate(version int) { // Cache settings delete(segment.Properties, "cache_version") - cacheTimeout := segment.Properties.GetInt("cache_timeout", 0) - if cacheTimeout != 0 { - segment.Cache = &cache.Config{ - Duration: cache.ToDuration(cacheTimeout * 60), - Strategy: cache.Folder, + segment.Cache = segment.migrateCache() + + segment.IncludeFolders = segment.migrateFolders(includeFolders) + segment.ExcludeFolders = segment.migrateFolders(excludeFolders) +} + +func (segment *Segment) hasProperty(property properties.Property) bool { + for key := range segment.Properties { + if key == property { + return true } } + return false +} + +func (segment *Segment) migrateCache() *cache.Config { + if !segment.hasProperty(cacheTimeout) { + return nil + } + + timeout := segment.Properties.GetInt(cacheTimeout, 0) + delete(segment.Properties, cacheTimeout) + + if timeout == 0 { + return nil + } + + return &cache.Config{ + Duration: cache.ToDuration(timeout * 60), + Strategy: cache.Folder, + } +} + +func (segment *Segment) migrateFolders(property properties.Property) []string { + if !segment.hasProperty(property) { + return []string{} + } + + array := segment.Properties.GetStringArray(property, []string{}) + delete(segment.Properties, property) + + return array } diff --git a/src/config/migrate_test.go b/src/config/migrate_test.go new file mode 100644 index 00000000..5cb1a7d8 --- /dev/null +++ b/src/config/migrate_test.go @@ -0,0 +1,55 @@ +package config + +import ( + "testing" + + "github.com/jandedobbeleer/oh-my-posh/src/cache" + "github.com/jandedobbeleer/oh-my-posh/src/properties" + + "github.com/stretchr/testify/assert" +) + +func TestMigrateCache(t *testing.T) { + cases := []struct { + Expected *cache.Config + Case string + Timeout int + }{ + {Case: "No timeout set"}, + {Case: "Timeout set to 0", Timeout: 0}, + {Case: "Timeout set to 10", Timeout: 10, Expected: &cache.Config{Duration: "10m0s", Strategy: cache.Folder}}, + } + + for _, tc := range cases { + segment := &Segment{ + Properties: properties.Map{ + cacheTimeout: tc.Timeout, + }, + } + + got := segment.migrateCache() + assert.Equal(t, tc.Expected, got, tc.Case) + } +} + +func TestMigrateFolders(t *testing.T) { + cases := []struct { + Case string + Folders []string + }{ + {Case: "No folders set"}, + {Case: "Empty folders", Folders: []string{}}, + {Case: "Folders set", Folders: []string{"/super/secret/project"}}, + } + + for _, tc := range cases { + segment := &Segment{ + Properties: properties.Map{ + excludeFolders: tc.Folders, + }, + } + + got := segment.migrateFolders(excludeFolders) + assert.Equal(t, tc.Folders, got, tc.Case) + } +} diff --git a/src/config/segment.go b/src/config/segment.go index c8fbf5f6..e2b42475 100644 --- a/src/config/segment.go +++ b/src/config/segment.go @@ -40,8 +40,8 @@ type Segment struct { env runtime.Environment Properties properties.Map `json:"properties,omitempty" toml:"properties,omitempty"` Cache *cache.Config `json:"cache,omitempty" toml:"cache,omitempty"` - Alias string `json:"alias,omitempty" toml:"alias,omitempty"` - LeadingPowerlineSymbol string `json:"leading_powerline_symbol,omitempty" toml:"leading_powerline_symbol,omitempty"` + Style SegmentStyle `json:"style,omitempty" toml:"style,omitempty"` + styleCache SegmentStyle name string LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"` TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"` @@ -52,20 +52,22 @@ type Segment struct { Background color.Ansi `json:"background" toml:"background"` Filler string `json:"filler,omitempty" toml:"filler,omitempty"` Type SegmentType `json:"type,omitempty" toml:"type,omitempty"` - Style SegmentStyle `json:"style,omitempty" toml:"style,omitempty"` - styleCache SegmentStyle - ForegroundTemplates template.List `json:"foreground_templates,omitempty" toml:"foreground_templates,omitempty"` - Tips []string `json:"tips,omitempty" toml:"tips,omitempty"` - BackgroundTemplates template.List `json:"background_templates,omitempty" toml:"background_templates,omitempty"` - Templates template.List `json:"templates,omitempty" toml:"templates,omitempty"` - MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"` - MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"` - Duration time.Duration `json:"-" toml:"-"` - NameLength int `json:"-" toml:"-"` - Interactive bool `json:"interactive,omitempty" toml:"interactive,omitempty"` - Enabled bool `json:"-" toml:"-"` - Newline bool `json:"newline,omitempty" toml:"newline,omitempty"` - InvertPowerline bool `json:"invert_powerline,omitempty" toml:"invert_powerline,omitempty"` + Alias string `json:"alias,omitempty" toml:"alias,omitempty"` + LeadingPowerlineSymbol string `json:"leading_powerline_symbol,omitempty" toml:"leading_powerline_symbol,omitempty"` + ForegroundTemplates template.List `json:"foreground_templates,omitempty" toml:"foreground_templates,omitempty"` + Tips []string `json:"tips,omitempty" toml:"tips,omitempty"` + BackgroundTemplates template.List `json:"background_templates,omitempty" toml:"background_templates,omitempty"` + Templates template.List `json:"templates,omitempty" toml:"templates,omitempty"` + ExcludeFolders []string `json:"exclude_folders,omitempty" toml:"exclude_folders,omitempty"` + IncludeFolders []string `json:"include_folders,omitempty" toml:"include_folders,omitempty"` + Duration time.Duration `json:"-" toml:"-"` + NameLength int `json:"-" toml:"-"` + MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"` + MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"` + Interactive bool `json:"interactive,omitempty" toml:"interactive,omitempty"` + Enabled bool `json:"-" toml:"-"` + Newline bool `json:"newline,omitempty" toml:"newline,omitempty"` + InvertPowerline bool `json:"invert_powerline,omitempty" toml:"invert_powerline,omitempty"` } func (segment *Segment) Name() string { @@ -299,28 +301,13 @@ func (segment *Segment) shouldIncludeFolder() bool { } func (segment *Segment) cwdIncluded() bool { - value, ok := segment.Properties[properties.IncludeFolders] - if !ok { - // IncludeFolders isn't specified, everything is included + if len(segment.IncludeFolders) == 0 { return true } - list := properties.ParseStringArray(value) - - if len(list) == 0 { - // IncludeFolders is an empty array, everything is included - return true - } - - return segment.env.DirMatchesOneOf(segment.env.Pwd(), list) + return segment.env.DirMatchesOneOf(segment.env.Pwd(), segment.IncludeFolders) } func (segment *Segment) cwdExcluded() bool { - value, ok := segment.Properties[properties.ExcludeFolders] - if !ok { - return false - } - - list := properties.ParseStringArray(value) - return segment.env.DirMatchesOneOf(segment.env.Pwd(), list) + return segment.env.DirMatchesOneOf(segment.env.Pwd(), segment.ExcludeFolders) } diff --git a/src/config/segment_test.go b/src/config/segment_test.go index 8f769458..14c41723 100644 --- a/src/config/segment_test.go +++ b/src/config/segment_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/jandedobbeleer/oh-my-posh/src/color" - "github.com/jandedobbeleer/oh-my-posh/src/properties" "github.com/jandedobbeleer/oh-my-posh/src/runtime" "github.com/jandedobbeleer/oh-my-posh/src/runtime/mock" "github.com/jandedobbeleer/oh-my-posh/src/segments" @@ -46,11 +45,11 @@ func TestParseTestConfig(t *testing.T) { "foreground": "#ffffff", "background": "#61AFEF", "properties": { - "style": "folder", - "exclude_folders": [ - "/super/secret/project" - ] - } + "style": "folder" + }, + "exclude_folders": [ + "/super/secret/project" + ] } ` segment := &Segment{} @@ -78,11 +77,9 @@ func TestShouldIncludeFolder(t *testing.T) { env.On("DirMatchesOneOf", cwd, []string{"Projects/oh-my-posh"}).Return(tc.Included) env.On("DirMatchesOneOf", cwd, []string{"Projects/nope"}).Return(tc.Excluded) segment := &Segment{ - Properties: properties.Map{ - properties.IncludeFolders: []string{"Projects/oh-my-posh"}, - properties.ExcludeFolders: []string{"Projects/nope"}, - }, - env: env, + IncludeFolders: []string{"Projects/oh-my-posh"}, + ExcludeFolders: []string{"Projects/nope"}, + env: env, } got := segment.shouldIncludeFolder() assert.Equal(t, tc.Expected, got, tc.Case) diff --git a/src/properties/map.go b/src/properties/map.go index 19a2ca1b..3408c4b2 100644 --- a/src/properties/map.go +++ b/src/properties/map.go @@ -25,10 +25,6 @@ type Property string const ( // Style indicates the style to use Style Property = "style" - // IncludeFolders indicates folders to be included for the segment logic - IncludeFolders Property = "include_folders" - // ExcludeFolders indicates folders to be excluded for the segment logic - ExcludeFolders Property = "exclude_folders" // FetchVersion decides whether to fetch the version number or not FetchVersion Property = "fetch_version" // AlwaysEnabled decides whether or not to always display the info diff --git a/src/segments/git.go b/src/segments/git.go index 1e3f48a8..c70c9214 100644 --- a/src/segments/git.go +++ b/src/segments/git.go @@ -338,10 +338,6 @@ func (g *Git) shouldDisplay() bool { return false } - if g.shouldIgnoreRootRepository(gitdir.ParentFolder) { - return false - } - g.setDir(gitdir.Path) if !gitdir.IsDir { diff --git a/src/segments/mercurial.go b/src/segments/mercurial.go index e3282387..3deaf315 100644 --- a/src/segments/mercurial.go +++ b/src/segments/mercurial.go @@ -80,10 +80,6 @@ func (hg *Mercurial) shouldDisplay() bool { return false } - if hg.shouldIgnoreRootRepository(hgdir.ParentFolder) { - return false - } - hg.setDir(hgdir.ParentFolder) hg.workingDir = hgdir.Path diff --git a/src/segments/plastic.go b/src/segments/plastic.go index c8782e15..fdab7a48 100644 --- a/src/segments/plastic.go +++ b/src/segments/plastic.go @@ -55,10 +55,6 @@ func (p *Plastic) Enabled() bool { return false } - if p.shouldIgnoreRootRepository(wkdir.ParentFolder) { - return false - } - if !wkdir.IsDir { return false } diff --git a/src/segments/sapling.go b/src/segments/sapling.go index 93d0db84..f9ee41f3 100644 --- a/src/segments/sapling.go +++ b/src/segments/sapling.go @@ -81,10 +81,6 @@ func (sl *Sapling) shouldDisplay() bool { return false } - if sl.shouldIgnoreRootRepository(slDir.ParentFolder) { - return false - } - sl.workingDir = slDir.Path sl.rootDir = slDir.Path // convert the worktree file path to a windows one when in a WSL shared folder diff --git a/src/segments/sapling_test.go b/src/segments/sapling_test.go index 63a72d31..dc8461bf 100644 --- a/src/segments/sapling_test.go +++ b/src/segments/sapling_test.go @@ -125,7 +125,6 @@ func TestShouldDisplay(t *testing.T) { HasSapling bool InRepo bool Expected bool - Excluded bool }{ { Case: "Sapling not installed", @@ -134,12 +133,6 @@ func TestShouldDisplay(t *testing.T) { Case: "Sapling installed, not in repo", HasSapling: true, }, - { - Case: "Sapling installed, in repo but ignored", - HasSapling: true, - InRepo: true, - Excluded: true, - }, { Case: "Sapling installed, in repo", HasSapling: true, @@ -158,19 +151,14 @@ func TestShouldDisplay(t *testing.T) { env.On("InWSLSharedDrive").Return(false) env.On("GOOS").Return(runtime.LINUX) env.On("Home").Return("/usr/home/sapling") - env.On("DirMatchesOneOf", fileInfo.ParentFolder, []string{"/sapling/repo"}).Return(tc.Excluded) if tc.InRepo { env.On("HasParentFilePath", ".sl", false).Return(fileInfo, nil) } else { env.On("HasParentFilePath", ".sl", false).Return(&runtime.FileInfo{}, errors.New("error")) } - props := &properties.Map{ - properties.ExcludeFolders: []string{"/sapling/repo"}, - } - sl := &Sapling{} - sl.Init(props, env) + sl.Init(&properties.Map{}, env) got := sl.shouldDisplay() assert.Equal(t, tc.Expected, got, tc.Case) diff --git a/src/segments/scm.go b/src/segments/scm.go index 8c356ba1..58f5b6c8 100644 --- a/src/segments/scm.go +++ b/src/segments/scm.go @@ -138,14 +138,6 @@ func (s *scm) formatBranch(branch string) string { return string(runes[0:maxLength]) + truncateSymbol } -func (s *scm) shouldIgnoreRootRepository(rootDir string) bool { - excludedFolders := s.props.GetStringArray(properties.ExcludeFolders, []string{}) - if len(excludedFolders) == 0 { - return false - } - return s.env.DirMatchesOneOf(rootDir, excludedFolders) -} - func (s *scm) FileContents(folder, file string) string { return strings.Trim(s.env.FileContent(folder+"/"+file), " \r\n") } diff --git a/src/segments/svn.go b/src/segments/svn.go index 85212f7a..392a95c0 100644 --- a/src/segments/svn.go +++ b/src/segments/svn.go @@ -78,10 +78,6 @@ func (s *Svn) shouldDisplay() bool { return false } - if s.shouldIgnoreRootRepository(Svndir.ParentFolder) { - return false - } - if Svndir.IsDir { s.workingDir = Svndir.Path s.rootDir = Svndir.Path diff --git a/themes/schema.json b/themes/schema.json index ca58e66f..6c6faf89 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -450,27 +450,7 @@ "type": "object", "title": "Segment Properties, used to change behavior/displaying", "description": "https://ohmyposh.dev/docs/configuration/segment#properties", - "default": {}, - "properties": { - "include_folders": { - "type": "array", - "title": "If specified, segment will only render in these folders", - "description": "https://ohmyposh.dev/docs/configuration/segment#include--exclude-folders", - "default": [], - "items": { - "type": "string" - } - }, - "exclude_folders": { - "type": "array", - "title": "Exclude rendering in these folders", - "description": "https://ohmyposh.dev/docs/configuration/segment#include--exclude-folders", - "default": [], - "items": { - "type": "string" - } - } - } + "default": {} }, "interactive": { "type": "boolean", @@ -484,6 +464,24 @@ "description": "https://ohmyposh.dev/docs/configuration/segment", "default": "" }, + "include_folders": { + "type": "array", + "title": "If specified, segment will only render in these folders", + "description": "https://ohmyposh.dev/docs/configuration/segment#include--exclude-folders", + "default": [], + "items": { + "type": "string" + } + }, + "exclude_folders": { + "type": "array", + "title": "Exclude rendering in these folders", + "description": "https://ohmyposh.dev/docs/configuration/segment#include--exclude-folders", + "default": [], + "items": { + "type": "string" + } + }, "cache": { "type": "object", "title": "Cache settings", diff --git a/website/docs/configuration/block.mdx b/website/docs/configuration/block.mdx index a47ec6eb..41968474 100644 --- a/website/docs/configuration/block.mdx +++ b/website/docs/configuration/block.mdx @@ -20,7 +20,7 @@ import Config from "@site/src/components/Config.js"; }} /> -## Properties +## Settings | Name | Type | | ------------------ | --------- | diff --git a/website/docs/configuration/example.mdx b/website/docs/configuration/example.mdx index e78adae4..1ab8fbd3 100644 --- a/website/docs/configuration/example.mdx +++ b/website/docs/configuration/example.mdx @@ -4,92 +4,91 @@ title: Sample sidebar_label: Sample --- -import Config from '@site/src/components/Config.js'; +import Config from "@site/src/components/Config.js"; -\ue0b0 \ue23a ", + properties: { + always_enabled: true, + }, + style: "diamond", + trailing_diamond: "\ue0b4", + type: "status", }, - "style": "powerline", - "type": "git" - }, - { - "background": "#00897b", - "background_templates": [ - "{{ if gt .Code 0 }}#e91e63{{ end }}" - ], - "foreground": "#ffffff", - "template": "\ue0b0 \ue23a ", - "properties": { - "always_enabled": true - }, - "style": "diamond", - "trailing_diamond": "\ue0b4", - "type": "status" - } - ], - "type": "prompt" - } - ], - "final_space": true, - "version": 2 -}}/> - + ], + type: "prompt", + }, + ], + final_space: true, + version: 2, + }} +/> diff --git a/website/docs/configuration/general.mdx b/website/docs/configuration/general.mdx index 94237ed1..6a03caee 100644 --- a/website/docs/configuration/general.mdx +++ b/website/docs/configuration/general.mdx @@ -12,7 +12,7 @@ A really simple configuration could look like this. The default format is `json` There's a [schema][schema] available which is kept up-to-date and helps with autocomplete and validation of the configuration. :::info -There are a few [themes][themes] available which are basically predefined configs. You can use these as they are, or as a +There are a few [themes][themes] available which are basically predefined configurations. You can use these as they are, or as a starting point to create your own configuration. ::: @@ -107,7 +107,7 @@ escape characters to see the prompt as it would be shown inside a prompt functio :::caution The command below will not persist the configuration for your shell but print the prompt in your terminal. If you want to use your own configuration permanently, adjust the prompt configuration to use your custom -theme. +configuration. ::: ```bash @@ -124,7 +124,7 @@ For example, the following is a valid `--config` flag: `--config 'https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json'` ::: -## General Settings +## Settings | Name | Type | Default | Description | | --------------------------- | ---------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -142,9 +142,9 @@ For example, the following is a valid `--config` flag: ### JSON Schema Validation -As mentioned above, Oh My Posh themes can utilize JSON Schema to validate their contents. Themes should include a link to +As mentioned above, Oh My Posh configurations can utilize JSON Schema to validate their contents. Configurations should include a link to the [external schema document][schema] which prescribes the appropriate structure and contents for various elements. If -your code editor is configured to use JSON Schema, it will compare your custom theme to the external document, and issue +your code editor is configured to use JSON Schema, it will compare your configuration to the external document, and issue warnings for discrepancies. For example, given the following code: @@ -166,7 +166,7 @@ warnings, and ignore them at your peril. ### Accepted Formats -Oh My Posh supports three file formats for themes: `json`, `yaml`, and `toml`. +Oh My Posh supports three file formats for configurations: `json`, `yaml`, and `toml`. Various converters exist to convert between these, although they aren't perfect and will require manual adjustment. Notably, the schema implementation for json is as follows: diff --git a/website/docs/configuration/segment.mdx b/website/docs/configuration/segment.mdx index 4e32a301..1c73b17c 100644 --- a/website/docs/configuration/segment.mdx +++ b/website/docs/configuration/segment.mdx @@ -32,6 +32,8 @@ understand how to configure a segment. }} /> +## Settings + | Name | Type | Description | | -------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `type` | `string` | takes the `string` value referencing which segment logic it needs to run (see [segments][segments] for possible values) | @@ -53,7 +55,9 @@ understand how to configure a segment. | `alias` | `string` | for use with [cross segment template properties][cstp] | | `min_width` | `int` | if the terminal width is smaller than this value, the segment will be hidden. For your terminal width, see `oh-my-posh get width`. Defaults to `0` (disable) | | `max_width` | `int` | if the terminal width exceeds this value, the segment will be hidden. For your terminal width, see `oh-my-posh get width`. Defaults to `0` (disable) | -| `cache` | `Cache` | how to cache the segment to avoid fetching information too much, see [Cache][cache] below | +| `cache` | `Cache` | how to cache the segment to avoid fetching information too much, see [below][cache] | +| `include_folders` | `[]string` | define which folders to include to enable the segment, see [below][include-exclude] | +| `exclude_folders` | `[]string` | define which folders to exclude to disable the segment, see [below][include-exclude] | :::warning In Bash/Zsh, when the property `interactive` is `true` for a segment, the prompt length calculation can be wrong @@ -142,22 +146,7 @@ regardless of the folder you're in. The session strategy will cache the segment based on the current shell session. Use this for segments you want to display at all times but don't want to refresh too often. -## Properties - -An array of Properties with a value. This is used inside of the segment logic to tweak what the output of the segment will be. -Segments have the ability to define their own Properties, but there are some shared ones being used by the engine which allow -you to customize the output even more. - -### Shared properties - -You can use these on any segment, the engine is responsible for adding them correctly. - -| Name | Type | -| ----------------- | ---------- | -| `include_folders` | `[]string` | -| `exclude_folders` | `[]string` | - -#### Include / Exclude Folders +## Include / Exclude Folders Sometimes you might want to have a segment only rendered in certain folders. If `include_folders` is specified, the segment will only be rendered when in one of those locations. If `exclude_folders` is specified, the segment @@ -165,17 +154,13 @@ will not be rendered when in one of the excluded locations. @@ -185,9 +170,7 @@ name. The following will match `/Users/posh/Projects/Foo` but not `/home/Users/p @@ -195,15 +178,12 @@ You can also combine these properties: -#### Notes - +:::info - Oh My Posh will accept both `/` and `\` as path separators for a folder and will match regardless of which is used by the current operating system. - Because the strings are evaluated as regular expressions, if you want to use a backslash (`\`) in a Windows @@ -213,6 +193,7 @@ You can also combine these properties: This means that for user Bill, who has a user account `Bill` on Windows and `bill` on Linux, `~/Foo` might match `C:\Users\Bill\Foo` or `C:\Users\Bill\foo` on Windows but only `/home/bill/Foo` on Linux. +::: ## Hiding segments @@ -255,3 +236,5 @@ To list the currently toggled segments, use `oh-my-posh get toggles`. [templates]: templates.mdx [color-templates]: /docs/configuration/colors#color-templates [cstp]: templates.mdx#cross-segment-template-properties +[cache]: #cache +[include-exclude]: #include--exclude-folders