Repository: JanDeDobbeleer/oh-my-posh
Pull request #1374 (2021-12-04 09:27:03)

template segments
### Prerequisites

- [x] I have read and understood the `CONTRIBUTING` guide
- [x] The commit message follows the [conventional commits][cc] guidelines
- [x] Tests for the changes have been added (for bug fixes/features)
- [x] Docs have been added / updated (for bug fixes/features)

### Description

Add more template properties to segments

<!---

Tips:

If you're not comfortable with working with Git, we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/docs/docs/segment-angular.md b/docs/docs/segment-angular.md
index c3072398..352d9580 100644
--- a/docs/docs/segment-angular.md
+++ b/docs/docs/segment-angular.md
@@ -35,11 +35,12 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-azfunc.md b/docs/docs/segment-azfunc.md
index 984c0fb4..2abc25c8 100644
--- a/docs/docs/segment-azfunc.md
+++ b/docs/docs/segment-azfunc.md
@@ -39,11 +39,12 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-crystal.md b/docs/docs/segment-crystal.md
index 8f995bc8..e21733cc 100644
--- a/docs/docs/segment-crystal.md
+++ b/docs/docs/segment-crystal.md
@@ -38,11 +38,12 @@ Display the currently active crystal version.
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-dart.md b/docs/docs/segment-dart.md
index 9eb1d0e7..631f029a 100644
--- a/docs/docs/segment-dart.md
+++ b/docs/docs/segment-dart.md
@@ -39,11 +39,12 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-dotnet.md b/docs/docs/segment-dotnet.md
index 83bb0c20..f46cb7c6 100644
--- a/docs/docs/segment-dotnet.md
+++ b/docs/docs/segment-dotnet.md
@@ -44,11 +44,12 @@ with [sprig][sprig] utilizing the properties below. Defaults does nothing(backwa
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-golang.md b/docs/docs/segment-golang.md
index 7790269c..1aacf4b3 100644
--- a/docs/docs/segment-golang.md
+++ b/docs/docs/segment-golang.md
@@ -38,8 +38,9 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
diff --git a/docs/docs/segment-java.md b/docs/docs/segment-java.md
index c13c3e0c..cb358d83 100644
--- a/docs/docs/segment-java.md
+++ b/docs/docs/segment-java.md
@@ -51,8 +51,9 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
diff --git a/docs/docs/segment-julia.md b/docs/docs/segment-julia.md
index 3a4a0ecc..74d705ac 100644
--- a/docs/docs/segment-julia.md
+++ b/docs/docs/segment-julia.md
@@ -38,11 +38,12 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-node.md b/docs/docs/segment-node.md
index ced7d23e..5b481ee6 100644
--- a/docs/docs/segment-node.md
+++ b/docs/docs/segment-node.md
@@ -26,18 +26,11 @@ Display the currently active node version.
 ## Properties
 
 - home_enabled: `boolean` - display the segment in the HOME folder or not - defaults to `false`
-- display_version: `boolean` - display the node version - defaults to `true`
-- display_error: `boolean` - show the error context when failing to retrieve the version information - defaults to `true`
 - missing_command_text: `string` - text to display when the command is missing - defaults to empty
 - display_mode: `string` - determines when the segment is displayed
   - `always`: The segment is always displayed
   - `files`: The segment is only displayed when `*.js`, `*.ts`, or `package.json` files are present (default)
-- enable_version_mismatch: `boolean` - color the segment when the version in `.nvmrc` doesn't match the
-returned node version
-- color_background: `boolean` - color the background or foreground for `version_mismatch_color` - defaults to `false`
-- version_mismatch_color: `string` [color][colors] - the color to use for `enable_version_mismatch` - defaults to
-segment's background or foreground color
-- display_package_manager: `boolean` - show whether the current project uses Yarn or NPM - defaults to `false`
+- fetch_package_manager: `boolean` - define if the current project uses Yarn or NPM - defaults to `false`
 - yarn_icon: `string` - the icon/text to display when using Yarn - defaults to ` \uF61A`
 - npm_icon: `string` - the icon/text to display when using NPM - defaults to ` \uE71E`
 - template: `string` - A go [text/template][go-text-template] template extended with [sprig][sprig] utilizing the
@@ -46,11 +39,14 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
+- `.PackageManagerIcon`: `string` - the Yarn on NPM icon when setting `fetch_package_manager` to `true`
+- `.Mismatch`: `boolean` - if the version in `.nvmrc` matches with `.Full`
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-php.md b/docs/docs/segment-php.md
index 47b8ea48..47b09fbc 100644
--- a/docs/docs/segment-php.md
+++ b/docs/docs/segment-php.md
@@ -40,11 +40,12 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-python.md b/docs/docs/segment-python.md
index 13bd1f81..b15bcd2d 100644
--- a/docs/docs/segment-python.md
+++ b/docs/docs/segment-python.md
@@ -27,7 +27,7 @@ Supports conda, virtualenv and pyenv.
 ## Properties
 
 - home_enabled: `boolean` - display the segment in the HOME folder or not - defaults to `false`
-- display_virtual_env: `boolean` - show the name of the virtualenv or not - defaults to `true`
+- fetch_virtual_env: `boolean` - fetch the name of the virtualenv or not - defaults to `true`
 - display_default: `boolean` - show the name of the virtualenv when it's default (`system`, `base`)
 or not - defaults to `true`
 - display_version: `boolean` - display the python version - defaults to `true`
@@ -44,12 +44,14 @@ properties below. Defaults to `{{ .Full }}`
 
 ## Template Properties
 
+- `.Venv`: `string` - the virtual environment name (if present)
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-ruby.md b/docs/docs/segment-ruby.md
index 5d812f68..c15e2643 100644
--- a/docs/docs/segment-ruby.md
+++ b/docs/docs/segment-ruby.md
@@ -38,11 +38,12 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-rust.md b/docs/docs/segment-rust.md
index c0f2a431..40a1ce56 100644
--- a/docs/docs/segment-rust.md
+++ b/docs/docs/segment-rust.md
@@ -38,11 +38,12 @@ properties below. Defaults to `{{ .Full }}`
 ## Template Properties
 
 - `.Full`: `string` - the full version
-- `.Major`: `string` - is the major version
-- `.Minor`: `string` - is the minor version
-- `.Patch`: `string` - is the patch version
-- `.Prerelease`: `string` - is the prerelease version
-- `.BuildMetadata`: `string` - is the build metadata
+- `.Major`: `string` - major number
+- `.Minor`: `string` - minor number
+- `.Patch`: `string` - patch number
+- `.Prerelease`: `string` - prerelease info text
+- `.BuildMetadata`: `string` - build metadata
+- `.Error`: `string` - when fetching the version string errors
 
 [go-text-template]: https://golang.org/pkg/text/template/
 [sprig]: https://masterminds.github.io/sprig/
diff --git a/docs/docs/segment-session.md b/docs/docs/segment-session.md
index 53ffee6c..fcb0b92b 100644
--- a/docs/docs/segment-session.md
+++ b/docs/docs/segment-session.md
@@ -13,21 +13,19 @@ Show the current user and host name.
 ```json
 {
   "type": "session",
-  "style": "diamond",
+  "style": "diamond", 
   "foreground": "#ffffff",
   "background": "#c386f1",
   "leading_diamond": "\uE0B6",
   "trailing_diamond": "\uE0B0",
   "properties": {
-    "template": "{{ .UserName }}"
+    "template": "{{ if .SSHSession }}\uF817 {{ end }}{{ .UserName }}"
   }
 }
 ```
 
 ## Properties
 
-- ssh_icon: `string` - text/icon to display first when in an active SSH session - defaults
-to `\uF817 `
 - template: `string` - A go [text/template][go-text-template] template extended with [sprig][sprig] utilizing the
 properties below.
 
diff --git a/src/config.go b/src/config.go
index 273c66c9..8a48c93c 100644
--- a/src/config.go
+++ b/src/config.go
@@ -225,8 +225,8 @@ func getDefaultConfig(info string) *Config {
 						Background:      "#6CA35E",
 						Foreground:      "#ffffff",
 						Properties: map[Property]interface{}{
-							Prefix:         " \uE718",
-							DisplayVersion: false,
+							Prefix:       " \uE718",
+							FetchVersion: false,
 						},
 					},
 					{
diff --git a/src/properties.go b/src/properties.go
index 477a3dd3..596cb5e8 100644
--- a/src/properties.go
+++ b/src/properties.go
@@ -23,8 +23,8 @@ const (
 	ExcludeFolders Property = "exclude_folders"
 	// IgnoreFolders duplicate of ExcludeFolders
 	IgnoreFolders Property = "ignore_folders"
-	// DisplayVersion show the version number or not
-	DisplayVersion Property = "display_version"
+	// FetchVersion fetch the version number or not
+	FetchVersion Property = "fetch_version"
 	// AlwaysEnabled decides whether or not to always display the info
 	AlwaysEnabled Property = "always_enabled"
 	// SegmentTemplate is the template to use to render the information
diff --git a/src/segment_deprecated.go b/src/segment_deprecated.go
index 8dd728aa..6436799c 100644
--- a/src/segment_deprecated.go
+++ b/src/segment_deprecated.go
@@ -17,12 +17,12 @@ const (
 
 // Properties
 
-func (p properties) getOneOfBool(property, legacyProperty Property) bool {
+func (p properties) getOneOfBool(property, legacyProperty Property, defaultValue bool) bool {
 	_, found := p[legacyProperty]
 	if found {
-		return p.getBool(legacyProperty, false)
+		return p.getBool(legacyProperty, defaultValue)
 	}
-	return p.getBool(property, false)
+	return p.getBool(property, defaultValue)
 }
 
 func (p properties) hasOneOf(properties ...Property) bool {
@@ -254,6 +254,8 @@ const (
 	DisplayUser Property = "display_user"
 	// DefaultUserName holds the default user of the platform
 	DefaultUserName Property = "default_user_name"
+	// SSHIcon is the icon used for SSH sessions
+	SSHIcon Property = "ssh_icon"
 
 	defaultUserEnvVar = "POSH_SESSION_DEFAULT_USER"
 )
@@ -304,3 +306,100 @@ func (s *session) getFormattedText() string {
 	}
 	return fmt.Sprintf("%s%s%s%s", sshIcon, s.UserName, separator, s.ComputerName)
 }
+
+// Language
+
+const (
+	// DisplayVersion show the version number or not
+	DisplayVersion Property = "display_version"
+	// VersionMismatchColor displays empty string by default
+	VersionMismatchColor Property = "version_mismatch_color"
+	// EnableVersionMismatch displays empty string by default
+	EnableVersionMismatch Property = "enable_version_mismatch"
+)
+
+func (l *language) string() string {
+	if !l.props.getOneOfBool(FetchVersion, DisplayVersion, true) {
+		return ""
+	}
+
+	err := l.setVersion()
+	if err != nil {
+		l.Error = err.Error()
+	}
+	displayError := l.props.getBool(DisplayError, true)
+	if err != nil && displayError {
+		return err.Error()
+	}
+	if err != nil {
+		return ""
+	}
+
+	segmentTemplate := l.props.getString(SegmentTemplate, "{{ .Full }}")
+	template := &textTemplate{
+		Template: segmentTemplate,
+		Context:  l.version,
+		Env:      l.env,
+	}
+	text, err := template.render()
+	if err != nil {
+		return err.Error()
+	}
+
+	if l.props.getBool(EnableHyperlink, false) {
+		versionURLTemplate := l.props.getString(VersionURLTemplate, "")
+		// backward compatibility
+		if versionURLTemplate == "" {
+			text = l.buildVersionURL(text)
+		} else {
+			template := &textTemplate{
+				Template: versionURLTemplate,
+				Context:  l.version,
+				Env:      l.env,
+			}
+			url, err := template.render()
+			if err != nil {
+				return err.Error()
+			}
+			text = url
+		}
+	}
+
+	if l.props.getBool(EnableVersionMismatch, false) {
+		l.setVersionFileMismatch()
+	}
+	return text
+}
+
+func (l *language) colorMismatch() {
+	if l.props.getBool(ColorBackground, false) {
+		l.props[BackgroundOverride] = l.props.getColor(VersionMismatchColor, l.props.getColor(BackgroundOverride, ""))
+		return
+	}
+	l.props[ForegroundOverride] = l.props.getColor(VersionMismatchColor, l.props.getColor(ForegroundOverride, ""))
+}
+
+// Python
+
+const (
+	// DisplayVirtualEnv shows or hides the virtual env
+	DisplayVirtualEnv Property = "display_virtual_env"
+)
+
+func (p *python) legacyString() string {
+	if p.Venv == "" {
+		return p.language.string()
+	}
+	version := p.language.string()
+	if version == "" {
+		return p.Venv
+	}
+	return fmt.Sprintf("%s %s", p.Venv, version)
+}
+
+// Node
+
+const (
+	// DisplayPackageManager shows if NPM or Yarn is used
+	DisplayPackageManager Property = "display_package_manager"
+)
diff --git a/src/segment_git.go b/src/segment_git.go
index 20df06f6..a9cebba3 100644
--- a/src/segment_git.go
+++ b/src/segment_git.go
@@ -184,20 +184,20 @@ func (g *git) shouldIgnoreRootRepository(rootDir string) bool {
 
 func (g *git) string() string {
 	statusColorsEnabled := g.props.getBool(StatusColorsEnabled, false)
-	displayStatus := g.props.getOneOfBool(FetchStatus, DisplayStatus)
+	displayStatus := g.props.getOneOfBool(FetchStatus, DisplayStatus, false)
 	if !displayStatus {
 		g.HEAD = g.getPrettyHEADName()
 	}
 	if displayStatus || statusColorsEnabled {
 		g.setGitStatus()
 	}
-	if g.Upstream != "" && g.props.getOneOfBool(FetchUpstreamIcon, DisplayUpstreamIcon) {
+	if g.Upstream != "" && g.props.getOneOfBool(FetchUpstreamIcon, DisplayUpstreamIcon, false) {
 		g.UpstreamIcon = g.getUpstreamIcon()
 	}
-	if g.props.getOneOfBool(FetchStashCount, DisplayStashCount) {
+	if g.props.getOneOfBool(FetchStashCount, DisplayStashCount, false) {
 		g.StashCount = g.getStashContext()
 	}
-	if g.props.getOneOfBool(FetchWorktreeCount, DisplayWorktreeCount) {
+	if g.props.getOneOfBool(FetchWorktreeCount, DisplayWorktreeCount, false) {
 		g.WorktreeCount = g.getWorktreeContext()
 	}
 	// use template if available
diff --git a/src/segment_language.go b/src/segment_language.go
index 2cfffa89..998c13e7 100644
--- a/src/segment_language.go
+++ b/src/segment_language.go
@@ -58,6 +58,8 @@ type language struct {
 	displayMode        string
 
 	version
+	Error    string
+	Mismatch bool
 }
 
 const (
@@ -73,34 +75,23 @@ const (
 	DisplayModeContext string = "context"
 	// MissingCommandText sets the text to display when the command is not present in the system
 	MissingCommandText Property = "missing_command_text"
-	// VersionMismatchColor displays empty string by default
-	VersionMismatchColor Property = "version_mismatch_color"
-	// EnableVersionMismatch displays empty string by default
-	EnableVersionMismatch Property = "enable_version_mismatch"
 	// HomeEnabled displays the segment in the HOME folder or not
 	HomeEnabled Property = "home_enabled"
 	// LanguageExtensions the list of extensions to validate
 	LanguageExtensions Property = "extensions"
 )
 
-func (l *language) string() string {
-	if !l.props.getBool(DisplayVersion, true) {
-		return ""
-	}
-
-	err := l.setVersion()
-	displayError := l.props.getBool(DisplayError, true)
-	if err != nil && displayError {
-		return err.Error()
-	}
-	if err != nil {
-		return ""
+func (l *language) renderTemplate(segmentTemplate string, context SegmentWriter) string {
+	if l.props.getBool(FetchVersion, true) {
+		err := l.setVersion()
+		if err != nil {
+			l.Error = err.Error()
+		}
 	}
 
-	segmentTemplate := l.props.getString(SegmentTemplate, "{{ .Full }}")
 	template := &textTemplate{
 		Template: segmentTemplate,
-		Context:  l.version,
+		Context:  context,
 		Env:      l.env,
 	}
 	text, err := template.render()
@@ -108,27 +99,22 @@ func (l *language) string() string {
 		return err.Error()
 	}
 
-	if l.props.getBool(EnableHyperlink, false) {
-		versionURLTemplate := l.props.getString(VersionURLTemplate, "")
-		// backward compatibility
-		if versionURLTemplate == "" {
-			text = l.buildVersionURL(text)
-		} else {
-			template := &textTemplate{
-				Template: versionURLTemplate,
-				Context:  l.version,
-				Env:      l.env,
-			}
-			url, err := template.render()
-			if err != nil {
-				return err.Error()
-			}
-			text = url
-		}
+	if !l.props.getBool(EnableHyperlink, false) {
+		return text
 	}
-
-	if l.props.getBool(EnableVersionMismatch, false) {
-		l.setVersionFileMismatch()
+	versionURLTemplate := l.props.getString(VersionURLTemplate, "")
+	// backward compatibility
+	if versionURLTemplate == "" {
+		return l.buildVersionURL(text)
+	}
+	template = &textTemplate{
+		Template: versionURLTemplate,
+		Context:  l.version,
+		Env:      l.env,
+	}
+	text, err = template.render()
+	if err != nil {
+		return err.Error()
 	}
 	return text
 }
@@ -216,14 +202,14 @@ func (l *language) inLanguageContext() bool {
 }
 
 func (l *language) setVersionFileMismatch() {
-	if l.matchesVersionFile == nil || l.matchesVersionFile() {
+	if l.matchesVersionFile == nil {
 		return
 	}
-	if l.props.getBool(ColorBackground, false) {
-		l.props[BackgroundOverride] = l.props.getColor(VersionMismatchColor, l.props.getColor(BackgroundOverride, ""))
+	l.Mismatch = !l.matchesVersionFile()
+	if !l.Mismatch {
 		return
 	}
-	l.props[ForegroundOverride] = l.props.getColor(VersionMismatchColor, l.props.getColor(ForegroundOverride, ""))
+	l.colorMismatch()
 }
 
 func (l *language) buildVersionURL(text string) string {
diff --git a/src/segment_node.go b/src/segment_node.go
index 1a3fc1f1..6c7203bf 100644
--- a/src/segment_node.go
+++ b/src/segment_node.go
@@ -3,9 +3,9 @@ package main
 import "fmt"
 
 type node struct {
-	packageManagerIcon string
-
 	language
+
+	PackageManagerIcon string
 }
 
 const (
@@ -13,13 +13,17 @@ const (
 	YarnIcon Property = "yarn_icon"
 	// NPMIcon illustrates NPM is used
 	NPMIcon Property = "npm_icon"
-	// DisplayPackageManager shows if NPM or Yarn is used
-	DisplayPackageManager Property = "display_package_manager"
+	// FetchPackageManager shows if NPM or Yarn is used
+	FetchPackageManager Property = "fetch_package_manager"
 )
 
 func (n *node) string() string {
-	version := n.language.string()
-	return fmt.Sprintf("%s%s", version, n.packageManagerIcon)
+	segmentTemplate := n.language.props.getString(SegmentTemplate, "")
+	if len(segmentTemplate) == 0 {
+		version := n.language.string()
+		return fmt.Sprintf("%s%s", version, n.PackageManagerIcon)
+	}
+	return n.language.renderTemplate(segmentTemplate, n)
 }
 
 func (n *node) init(props properties, env environmentInfo) {
@@ -45,15 +49,15 @@ func (n *node) enabled() bool {
 }
 
 func (n *node) loadContext() {
-	if !n.language.props.getBool(DisplayPackageManager, false) {
+	if !n.language.props.getOneOfBool(FetchPackageManager, DisplayPackageManager, false) {
 		return
 	}
 	if n.language.env.hasFiles("yarn.lock") {
-		n.packageManagerIcon = n.language.props.getString(YarnIcon, " \uF61A")
+		n.PackageManagerIcon = n.language.props.getString(YarnIcon, " \uF61A")
 		return
 	}
 	if n.language.env.hasFiles("package-lock.json") || n.language.env.hasFiles("package.json") {
-		n.packageManagerIcon = n.language.props.getString(NPMIcon, " \uE71E")
+		n.PackageManagerIcon = n.language.props.getString(NPMIcon, " \uE71E")
 	}
 }
 
diff --git a/src/segment_python.go b/src/segment_python.go
index c36010ed..738e1939 100644
--- a/src/segment_python.go
+++ b/src/segment_python.go
@@ -1,26 +1,22 @@
 package main
 
-import "fmt"
-
 type python struct {
 	language
-	venvName string
+
+	Venv string
 }
 
 const (
-	// DisplayVirtualEnv shows or hides the virtual env
-	DisplayVirtualEnv Property = "display_virtual_env"
+	// FetchVirtualEnv fetches the virtual env
+	FetchVirtualEnv Property = "fetch_virtual_env"
 )
 
 func (p *python) string() string {
-	if p.venvName == "" {
-		return p.language.string()
-	}
-	version := p.language.string()
-	if version == "" {
-		return p.venvName
+	segmentTemplate := p.language.props.getString(SegmentTemplate, "")
+	if len(segmentTemplate) == 0 {
+		return p.legacyString()
 	}
-	return fmt.Sprintf("%s %s", p.venvName, version)
+	return p.language.renderTemplate(segmentTemplate, p)
 }
 
 func (p *python) init(props properties, env environmentInfo) {
@@ -53,7 +49,7 @@ func (p *python) enabled() bool {
 }
 
 func (p *python) loadContext() {
-	if !p.language.props.getBool(DisplayVirtualEnv, true) {
+	if !p.language.props.getOneOfBool(DisplayVirtualEnv, FetchVirtualEnv, true) {
 		return
 	}
 	venvVars := []string{
@@ -67,14 +63,14 @@ func (p *python) loadContext() {
 		venv = p.language.env.getenv(venvVar)
 		name := base(venv, p.language.env)
 		if p.canUseVenvName(name) {
-			p.venvName = name
+			p.Venv = name
 			break
 		}
 	}
 }
 
 func (p *python) inContext() bool {
-	return p.venvName != ""
+	return p.Venv != ""
 }
 
 func (p *python) canUseVenvName(name string) bool {
diff --git a/src/segment_session.go b/src/segment_session.go
index 4bfe754a..06ab4f76 100644
--- a/src/segment_session.go
+++ b/src/segment_session.go
@@ -12,14 +12,10 @@ type session struct {
 	SSHSession   bool
 	Root         bool
 
+	// Deprecated
 	DefaultUserName string
 }
 
-const (
-	// SSHIcon is the icon used for SSH sessions
-	SSHIcon Property = "ssh_icon"
-)
-
 func (s *session) enabled() bool {
 	s.SSHSession = s.activeSSHSession()
 	segmentTemplate := s.props.getString(SegmentTemplate, "")
diff --git a/themes/atomic.omp.json b/themes/atomic.omp.json
index 2ad9ab9f..ff1c5a3a 100644
--- a/themes/atomic.omp.json
+++ b/themes/atomic.omp.json
@@ -288,7 +288,6 @@
           "foreground": "#ffffff",
           "properties": {
             "prefix": " \uf5ef ",
-            "postfix": " ",
             "time_format": "_2,15:04"
         }
         }
diff --git a/themes/cert.omp.json b/themes/cert.omp.json
index 72abe209..c78c7b05 100644
--- a/themes/cert.omp.json
+++ b/themes/cert.omp.json
@@ -14,7 +14,6 @@
           "foreground": "#fff",
           "properties": {
             "prefix": "",
-            "postfix": " ",
             "template": "{{ .UserName }}"
           }
         },
@@ -26,9 +25,7 @@
           "background": "green",
           "foreground": "#fff",
           "properties": {
-            "style": "folder",
-            "prefix": " ",
-            "postfix": " "
+            "style": "folder"
           }
         },
         {
@@ -53,9 +50,7 @@
           "foreground": "#fff",
           "background": "lightMagenta",
           "properties": {
-            "time_format": "15:04",
-            "prefix": " ",
-            "postfix": " "
+            "time_format": "15:04"
           }
         }
       ]
diff --git a/themes/fish.omp.json b/themes/fish.omp.json
index 4594e5e1..cacb88e8 100644
--- a/themes/fish.omp.json
+++ b/themes/fish.omp.json
@@ -36,8 +36,7 @@
             "folder_icon": "\uF115",
             "folder_separator_icon": " \uE0B1 ",
             "style": "full",
-            "prefix": "<transparent>\uE0B0</> ",
-            "postfix": " "
+            "prefix": "<transparent>\uE0B0</> "
           }
         },
         {
@@ -47,7 +46,6 @@
           "background": "#007ACC",
           "properties": {
             "prefix": "<#ffffff>\uE0B1</> ",
-            "postfix": " ",
             "template": "{{ .HEAD }}"
           }
         },
diff --git a/themes/gmay.omp.json b/themes/gmay.omp.json
index fa43804a..0c5cfe79 100644
--- a/themes/gmay.omp.json
+++ b/themes/gmay.omp.json
@@ -39,8 +39,7 @@
           "foreground": "#193549",
           "background": "#4caf50",
           "properties": {
-            "time_format": "2006-01-02 15:04:05",
-            "prefix": " "
+            "time_format": "2006-01-02 15:04:05"
           }
         },
 
diff --git a/themes/hunk.omp.json b/themes/hunk.omp.json
index 618adaa2..0ab5832b 100644
--- a/themes/hunk.omp.json
+++ b/themes/hunk.omp.json
@@ -84,8 +84,7 @@
           "foreground": "#ffffff",
           "background": "#83769c",
           "properties": {
-            "always_enabled": true,
-            "prefix": " "
+            "always_enabled": true
           }
         },
         {
@@ -96,7 +95,6 @@
           "foreground": "#FFD700",
           "background": "#86BBD8",
           "properties": {
-            "prefix": " ",
             "postfix": "",
             "root_icon": "⚡"
           }
@@ -118,11 +116,7 @@
           "trailing_diamond": "\uE0B4",
           "invert_powerline": true,
           "foreground": "#ffffff",
-          "background": "#33658A",
-          "properties": {
-            "prefix": " ",
-            "postfix": " "
-          }
+          "background": "#33658A"
         }
       ]
     },
diff --git a/themes/iterm2.omp.json b/themes/iterm2.omp.json
index 09d675c4..76a96d56 100644
--- a/themes/iterm2.omp.json
+++ b/themes/iterm2.omp.json
@@ -16,7 +16,6 @@
           "foreground": "#fff",
           "background": "#003543",
           "properties": {
-            "prefix": " ",
             "windows": "\uF179",
             "postfix": ""
           }
@@ -105,9 +104,9 @@
           "background": "#0087D8",
           "properties": {
             "prefix": " \uE235 ",
-            "display_version": false,
             "display_mode": "context",
-            "display_virtual_env": true
+            "fetch_virtual_env": true,
+            "template": "{{ .Venv }}"
           }
         },
         {
diff --git a/themes/jandedobbeleer.omp.json b/themes/jandedobbeleer.omp.json
index c6719220..196894b8 100644
--- a/themes/jandedobbeleer.omp.json
+++ b/themes/jandedobbeleer.omp.json
@@ -91,9 +91,9 @@
           "background": "#FFDE57",
           "properties": {
             "prefix": " \uE235 ",
-            "display_version": true,
             "display_mode": "files",
-            "display_virtual_env": false
+            "fetch_virtual_env": false,
+            "template": "{{ .Full }}"
           }
         },
         {
diff --git a/themes/jblab_2021.omp.json b/themes/jblab_2021.omp.json
index 2b528975..44c1c2e3 100644
--- a/themes/jblab_2021.omp.json
+++ b/themes/jblab_2021.omp.json
@@ -16,7 +16,6 @@
           "leading_diamond": "\uE0B6",
           "trailing_diamond": "\uE0B0",
           "properties": {
-            "prefix": " ",
             "wsl": "",
             "wsl_separator": ""
           },
@@ -59,7 +58,6 @@
             "fetch_stash_count": true,
             "fetch_status": true,
             "fetch_upstream_icon": true,
-            "prefix": " ",
             "template": "{{ .UpstreamIcon }}{{ .HEAD }}{{ if .Staging.Changed }} \uF046 {{ .Staging.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Working.Changed }} \uF044 {{ .Working.String }}{{ end }}{{ if gt .StashCount 0 }} \uF692 {{ .StashCount }}{{ end }}"
           },
           "style": "powerline",
diff --git a/themes/jv_sitecorian.omp.json b/themes/jv_sitecorian.omp.json
index dafdafa9..aca523e8 100644
--- a/themes/jv_sitecorian.omp.json
+++ b/themes/jv_sitecorian.omp.json
@@ -59,7 +59,6 @@
             "style": "mixed",
             "home_icon": "\uf7dd  ",
             "prefix": "",
-            "postfix": " ",
             "enable_hyperlink": true
           }
         },
diff --git a/themes/material.omp.json b/themes/material.omp.json
index 0588c9f4..1e5d433b 100644
--- a/themes/material.omp.json
+++ b/themes/material.omp.json
@@ -41,7 +41,6 @@
           "foreground": "#DCB977",
           "properties": {
             "template": "\uF119",
-            "prefix": " ",
             "postfix": ""
           }
         },
diff --git a/themes/mojada.omp.json b/themes/mojada.omp.json
index 8c9e56cb..911d657d 100644
--- a/themes/mojada.omp.json
+++ b/themes/mojada.omp.json
@@ -13,7 +13,6 @@
           "foreground": "#0077c2",
           "background": "#fbfbfb",
           "properties": {
-            "postfix": " ",
             "macos": "\uEF179",
             "windows": "\uF17A",
             "linux": "\uF17C",
@@ -87,7 +86,6 @@
           "style": "plain",
           "foreground": "#FFD54F",
           "properties": {
-            "prefix": " ",
             "text": "{{if .Root}}#{{else}}${{end}}",
             "postfix": ""
           }
diff --git a/themes/negligible.omp.json b/themes/negligible.omp.json
index 2db46427..5f9f8d38 100644
--- a/themes/negligible.omp.json
+++ b/themes/negligible.omp.json
@@ -69,9 +69,9 @@
           "foreground": "#4584b6",
           "properties": {
             "prefix": "| \uE235 ",
-            "display_version": false,
             "display_mode": "context",
-            "display_virtual_env": true
+            "fetch_virtual_env": true,
+            "template": "{{ .Venv }}"
           }
         },
         {
diff --git a/themes/night-owl.omp.json b/themes/night-owl.omp.json
index 7270355e..1fac9be7 100644
--- a/themes/night-owl.omp.json
+++ b/themes/night-owl.omp.json
@@ -82,7 +82,6 @@
           "foreground": "#d6deeb",
           "background": "#575656",
           "properties": {
-            "postfix": " ",
             "threshold": 0,
             "style": "roundrock"
           }
diff --git a/themes/pararussel.omp.json b/themes/pararussel.omp.json
index 93e71719..cd2b3325 100644
--- a/themes/pararussel.omp.json
+++ b/themes/pararussel.omp.json
@@ -39,8 +39,7 @@
           "style": "plain",
           "foreground": "#DCB977",
           "properties": {
-            "template": "\uF119",
-            "prefix": " "
+            "template": "\uF119"
           }
         }
       ]
diff --git a/themes/patriksvensson.omp.json b/themes/patriksvensson.omp.json
index 6ad82344..cb025b81 100644
--- a/themes/patriksvensson.omp.json
+++ b/themes/patriksvensson.omp.json
@@ -14,7 +14,6 @@
           "foreground": "red",
           "properties": {
             "prefix": "",
-            "postfix": " ",
             "root_icon": "\uF0E7"
           }
         },
diff --git a/themes/powerlevel10k_classic.omp.json b/themes/powerlevel10k_classic.omp.json
index d3f90172..0dc82f1b 100644
--- a/themes/powerlevel10k_classic.omp.json
+++ b/themes/powerlevel10k_classic.omp.json
@@ -26,8 +26,7 @@
           "foreground": "#26C6DA",
           "background": "#546E7A",
           "properties": {
-            "style": "full",
-            "postfix": " "
+            "style": "full"
           }
         },
         {
diff --git a/themes/powerlevel10k_modern.omp.json b/themes/powerlevel10k_modern.omp.json
index 39d8f536..887886fd 100644
--- a/themes/powerlevel10k_modern.omp.json
+++ b/themes/powerlevel10k_modern.omp.json
@@ -30,8 +30,7 @@
           "background": "#0000ff",
           "powerline_symbol": "\uE0B4",
           "properties": {
-            "style": "full",
-            "postfix": " "
+            "style": "full"
           }
         },
         {
@@ -41,7 +40,6 @@
           "background": "#D4E157",
           "powerline_symbol": "\uE0B4",
           "properties": {
-            "prefix": " ",
             "template": "{{ .HEAD }}"
           }
         }
diff --git a/themes/powerlevel10k_rainbow.omp.json b/themes/powerlevel10k_rainbow.omp.json
index 4923f4d6..70537bdc 100644
--- a/themes/powerlevel10k_rainbow.omp.json
+++ b/themes/powerlevel10k_rainbow.omp.json
@@ -95,9 +95,9 @@
           "background": "#FFDE57",
           "properties": {
             "postfix": " \uE235 ",
-            "display_version": true,
             "display_mode": "files",
-            "display_virtual_env": false
+            "fetch_virtual_env": false,
+            "template": "{{ .Full }}"
           }
         },
         {
@@ -172,7 +172,6 @@
           "background": "#000000",
           "background_templates": ["{{ if gt .Code 0 }}#cc2222{{ end }}"],
           "properties": {
-            "prefix": " ",
             "always_enabled": true,
             "template": "{{ if gt .Code 0 }}{{ .Text }}{{ else }}✔{{ end }}"
           }
diff --git a/themes/robbyrussel.omp.json b/themes/robbyrussel.omp.json
index a6cfa52e..f671d1cb 100644
--- a/themes/robbyrussel.omp.json
+++ b/themes/robbyrussel.omp.json
@@ -40,7 +40,6 @@
           "foreground": "#BF616A",
           "properties": {
             "template": "\u2717",
-            "prefix": " ",
             "postfix": ""
           }
         }
diff --git a/themes/rudolfs-dark.omp.json b/themes/rudolfs-dark.omp.json
index 873a559b..78772bb4 100644
--- a/themes/rudolfs-dark.omp.json
+++ b/themes/rudolfs-dark.omp.json
@@ -18,9 +18,7 @@
           "foreground": "#ffffff",
           "properties": {
             "wsl": "",
-            "wsl_separator": "",
-            "prefix": " ",
-            "postfix": " "
+            "wsl_separator": ""
           }
         },
         {
@@ -42,8 +40,7 @@
             "max_depth": 2,
             "folder_icon": "\uF115",
             "folder_separator_icon": " \uE0B1 ",
-            "style": "agnoster_short",
-            "postfix": " "
+            "style": "agnoster_short"
           }
         },
         {
diff --git a/themes/rudolfs-light.omp.json b/themes/rudolfs-light.omp.json
index 1ee46d93..68fbeb2a 100644
--- a/themes/rudolfs-light.omp.json
+++ b/themes/rudolfs-light.omp.json
@@ -18,9 +18,7 @@
           "foreground": "#EF7D00",
           "properties": {
             "wsl": "",
-            "wsl_separator": "",
-            "prefix": " ",
-            "postfix": " "
+            "wsl_separator": ""
           }
         },
         {
diff --git a/themes/schema.json b/themes/schema.json
index 9e114529..bc08243d 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -33,10 +33,10 @@
         "type": "string"
       }
     },
-    "display_version": {
+    "fetch_version": {
       "type": "boolean",
-      "title": "Display Version",
-      "description": "Show or hide the version number",
+      "title": "Fetch Version",
+      "description": "Fetch the version number",
       "default": true
     },
     "enable_hyperlink": {
@@ -402,8 +402,8 @@
                     "description": "Text/icon that is displayed when the active .NET SDK version (e.g., one specified by global.json) is not installed/supported",
                     "default": " \uE77F "
                   },
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -635,8 +635,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -664,8 +664,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -693,8 +693,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -722,8 +722,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -751,8 +751,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -780,8 +780,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -806,8 +806,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -832,8 +832,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -916,8 +916,11 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "template": {
+                    "$ref": "#/definitions/template"
+                  },
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -925,10 +928,10 @@
                   "missing_command_text": {
                     "$ref": "#/definitions/missing_command_text"
                   },
-                  "display_package_manager": {
+                  "fetch_package_manager": {
                     "type": "boolean",
-                    "title": "Display Package Manager",
-                    "description": "Show whether the current project uses Yarn or NPM",
+                    "title": "Fetch Display Package Manager",
+                    "description": "Assigns the Yarn or NPM icon to .PackageManagerIcon",
                     "default": false
                   },
                   "yarn_icon": {
@@ -963,8 +966,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
@@ -1240,19 +1243,10 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
-                  },
-                  "display_default": {
+                  "fetch_virtual_env": {
                     "type": "boolean",
-                    "title": "Display Default Env",
-                    "description": "Show the name of the virtualenv when it's default (`system`, `base`)",
-                    "default": true
-                  },
-                  "display_virtual_env": {
-                    "type": "boolean",
-                    "title": "Display Virtual Env",
-                    "description": "Show the name of the virtualenv or not",
+                    "title": "Fetch Virtual Env",
+                    "description": "Fetch the name of the virtualenv or not",
                     "default": true
                   },
                   "display_mode": {
@@ -1263,6 +1257,9 @@
                   },
                   "enable_hyperlink": {
                     "$ref": "#/definitions/enable_hyperlink"
+                  },
+                  "template": {
+                    "$ref": "#/definitions/template"
                   }
                 }
               }
@@ -1639,8 +1636,8 @@
             "properties": {
               "properties": {
                 "properties": {
-                  "display_version": {
-                    "$ref": "#/definitions/display_version"
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
                   },
                   "display_mode": {
                     "$ref": "#/definitions/display_mode"
diff --git a/themes/unicorn.omp.json b/themes/unicorn.omp.json
index 3008b02a..c136c02f 100644
--- a/themes/unicorn.omp.json
+++ b/themes/unicorn.omp.json
@@ -17,7 +17,6 @@
           "foreground": "#fff",
           "background": "#003543",
           "properties": {
-            "prefix": " ",
             "windows": "\uE62A ",
             "postfix": ""
           }
diff --git a/themes/wopian.omp.json b/themes/wopian.omp.json
index d2bf0ad8..3ac78b53 100644
--- a/themes/wopian.omp.json
+++ b/themes/wopian.omp.json
@@ -46,7 +46,6 @@
           "foreground": "#68a063",
           "properties": {
             "display_version": true,
-            "prefix": " ",
             "postfix": "",
             "display_mode": "files",
             "display_package_manager": true,
@@ -61,7 +60,6 @@
           "properties": {
             "display_version": true,
             "display_mode": "files",
-            "prefix": " ",
             "postfix": ""
           }
         },
@@ -71,7 +69,6 @@
           "foreground": "#DE3F24",
           "properties": {
             "display_version": true,
-            "prefix": " ",
             "postfix": "",
             "display_mode": "files"
           }
@@ -81,11 +78,10 @@
           "style": "plain",
           "foreground": "#FED142",
           "properties": {
-            "display_virtual_env": false,
-            "display_version": true,
-            "prefix": " ",
+            "fetch_virtual_env": false,
             "postfix": "",
-            "display_mode": "context"
+            "display_mode": "context",
+            "template": "{{ .Full }}"
           }
         },
         {


Test patch:
diff --git a/src/segment_deprecated_test.go b/src/segment_deprecated_test.go
index 367e5e33..5f230048 100644
--- a/src/segment_deprecated_test.go
+++ b/src/segment_deprecated_test.go
@@ -596,3 +596,108 @@ func TestPropertySessionSegment(t *testing.T) {
 		}
 	}
 }
+
+// Language
+
+func TestLanguageVersionMismatch(t *testing.T) {
+	cases := []struct {
+		Case            string
+		Enabled         bool
+		Mismatch        bool
+		ExpectedColor   string
+		ColorBackground bool
+	}{
+		{Case: "Mismatch - Foreground color", Enabled: true, Mismatch: true, ExpectedColor: "#566777"},
+		{Case: "Mismatch - Background color", Enabled: true, Mismatch: true, ExpectedColor: "#566777", ColorBackground: true},
+		{Case: "Disabled", Enabled: false},
+		{Case: "No mismatch", Enabled: true, Mismatch: false},
+	}
+	for _, tc := range cases {
+		props := map[Property]interface{}{
+			EnableVersionMismatch: tc.Enabled,
+			VersionMismatchColor:  tc.ExpectedColor,
+			ColorBackground:       tc.ColorBackground,
+		}
+		var matchesVersionFile func() bool
+		switch tc.Mismatch {
+		case true:
+			matchesVersionFile = func() bool {
+				return false
+			}
+		default:
+			matchesVersionFile = func() bool {
+				return true
+			}
+		}
+		args := &languageArgs{
+			commands: []*cmd{
+				{
+					executable: "unicorn",
+					args:       []string{"--version"},
+					regex:      "(?P<version>.*)",
+				},
+			},
+			extensions:         []string{uni, corn},
+			enabledExtensions:  []string{uni, corn},
+			enabledCommands:    []string{"unicorn"},
+			version:            universion,
+			properties:         props,
+			matchesVersionFile: matchesVersionFile,
+		}
+		lang := bootStrapLanguageTest(args)
+		assert.True(t, lang.enabled(), tc.Case)
+		assert.Equal(t, universion, lang.string(), tc.Case)
+		if tc.ColorBackground {
+			assert.Equal(t, tc.ExpectedColor, lang.props[BackgroundOverride], tc.Case)
+			return
+		}
+		assert.Equal(t, tc.ExpectedColor, lang.props.getColor(ForegroundOverride, ""), tc.Case)
+	}
+}
+
+// Python
+
+func TestPythonVirtualEnv(t *testing.T) {
+	cases := []struct {
+		Case                string
+		Expected            string
+		ExpectedDisabled    bool
+		VirtualEnvName      string
+		CondaEnvName        string
+		CondaDefaultEnvName string
+		PyEnvName           string
+		FetchVersion        bool
+		DisplayDefault      bool
+	}{
+		{Case: "VENV", Expected: "VENV", VirtualEnvName: "VENV"},
+		{Case: "CONDA", Expected: "CONDA", CondaEnvName: "CONDA"},
+		{Case: "CONDA default", Expected: "CONDA", CondaDefaultEnvName: "CONDA"},
+		{Case: "Display Base", Expected: "base", CondaDefaultEnvName: "base", DisplayDefault: true},
+		{Case: "Hide base", Expected: "", CondaDefaultEnvName: "base", ExpectedDisabled: true},
+		{Case: "PYENV", Expected: "PYENV", PyEnvName: "PYENV"},
+		{Case: "PYENV Version", Expected: "PYENV 3.8.4", PyEnvName: "PYENV", FetchVersion: true},
+	}
+
+	for _, tc := range cases {
+		env := new(MockedEnvironment)
+		env.On("hasCommand", "python").Return(true)
+		env.On("runCommand", "python", []string{"--version"}).Return("Python 3.8.4", nil)
+		env.On("hasFiles", "*.py").Return(true)
+		env.On("getenv", "VIRTUAL_ENV").Return(tc.VirtualEnvName)
+		env.On("getenv", "CONDA_ENV_PATH").Return(tc.CondaEnvName)
+		env.On("getenv", "CONDA_DEFAULT_ENV").Return(tc.CondaDefaultEnvName)
+		env.On("getenv", "PYENV_VERSION").Return(tc.PyEnvName)
+		env.On("getPathSeperator", nil).Return("")
+		env.On("getcwd", nil).Return("/usr/home/project")
+		env.On("homeDir", nil).Return("/usr/home")
+		var props properties = map[Property]interface{}{
+			FetchVersion:      tc.FetchVersion,
+			DisplayVirtualEnv: true,
+			DisplayDefault:    tc.DisplayDefault,
+		}
+		python := &python{}
+		python.init(props, env)
+		assert.Equal(t, !tc.ExpectedDisabled, python.enabled(), tc.Case)
+		assert.Equal(t, tc.Expected, python.string(), tc.Case)
+	}
+}
diff --git a/src/segment_dotnet_test.go b/src/segment_dotnet_test.go
index 37d89bbb..e862b1e1 100644
--- a/src/segment_dotnet_test.go
+++ b/src/segment_dotnet_test.go
@@ -29,7 +29,7 @@ func bootStrapDotnetTest(args *dotnetArgs) *dotnet {
 	env.On("getcwd", nil).Return("/usr/home/project")
 	env.On("homeDir", nil).Return("/usr/home")
 	var props properties = map[Property]interface{}{
-		DisplayVersion:               args.displayVersion,
+		FetchVersion:                 args.displayVersion,
 		UnsupportedDotnetVersionIcon: args.unsupportedIcon,
 	}
 	dotnet := &dotnet{}
diff --git a/src/segment_golang_test.go b/src/segment_golang_test.go
index 5391a915..e95976dd 100644
--- a/src/segment_golang_test.go
+++ b/src/segment_golang_test.go
@@ -22,7 +22,7 @@ func getMockedLanguageEnv(params *mockedLanguageParams) (*MockedEnvironment, pro
 	env.On("getcwd", nil).Return("/usr/home/project")
 	env.On("homeDir", nil).Return("/usr/home")
 	var props properties = map[Property]interface{}{
-		DisplayVersion: true,
+		FetchVersion: true,
 	}
 	return env, props
 }
diff --git a/src/segment_java_test.go b/src/segment_java_test.go
index f3829153..590ba032 100644
--- a/src/segment_java_test.go
+++ b/src/segment_java_test.go
@@ -67,7 +67,7 @@ func TestJava(t *testing.T) {
 			env.On("getenv", "JAVA_HOME").Return("")
 		}
 		var props properties = map[Property]interface{}{
-			DisplayVersion: true,
+			FetchVersion: true,
 		}
 		j := &java{}
 		j.init(props, env)
diff --git a/src/segment_language_test.go b/src/segment_language_test.go
index 529f2d04..d5c0f6a1 100644
--- a/src/segment_language_test.go
+++ b/src/segment_language_test.go
@@ -79,7 +79,7 @@ func TestLanguageFilesFoundButNoCommandAndVersionAndDisplayVersion(t *testing.T)
 
 func TestLanguageFilesFoundButNoCommandAndVersionAndDontDisplayVersion(t *testing.T) {
 	props := map[Property]interface{}{
-		DisplayVersion: false,
+		FetchVersion: false,
 	}
 	args := &languageArgs{
 		commands: []*cmd{
@@ -229,7 +229,7 @@ func TestLanguageEnabledAllExtensionsFound(t *testing.T) {
 
 func TestLanguageEnabledNoVersion(t *testing.T) {
 	props := map[Property]interface{}{
-		DisplayVersion: false,
+		FetchVersion: false,
 	}
 	args := &languageArgs{
 		commands: []*cmd{
@@ -252,7 +252,7 @@ func TestLanguageEnabledNoVersion(t *testing.T) {
 
 func TestLanguageEnabledMissingCommand(t *testing.T) {
 	props := map[Property]interface{}{
-		DisplayVersion: false,
+		FetchVersion: false,
 	}
 	args := &languageArgs{
 		commands:          []*cmd{},
@@ -269,7 +269,7 @@ func TestLanguageEnabledMissingCommand(t *testing.T) {
 
 func TestLanguageEnabledNoVersionData(t *testing.T) {
 	props := map[Property]interface{}{
-		DisplayVersion: true,
+		FetchVersion: true,
 	}
 	args := &languageArgs{
 		commands: []*cmd{
@@ -496,59 +496,3 @@ func TestLanguageHyperlinkEnabledMoreParamInTemplate(t *testing.T) {
 	assert.True(t, lang.enabled())
 	assert.Equal(t, "1.3.307", lang.string())
 }
-
-func TestLanguageVersionMismatch(t *testing.T) {
-	cases := []struct {
-		Case            string
-		Enabled         bool
-		Mismatch        bool
-		ExpectedColor   string
-		ColorBackground bool
-	}{
-		{Case: "Disabled", Enabled: false},
-		{Case: "Mismatch - Foreground color", Enabled: true, Mismatch: true, ExpectedColor: "#566777"},
-		{Case: "Mismatch - Background color", Enabled: true, Mismatch: true, ExpectedColor: "#566777", ColorBackground: true},
-		{Case: "No mismatch", Enabled: true, Mismatch: false},
-	}
-	for _, tc := range cases {
-		props := map[Property]interface{}{
-			EnableVersionMismatch: tc.Enabled,
-			VersionMismatchColor:  tc.ExpectedColor,
-			ColorBackground:       tc.ColorBackground,
-		}
-		var matchesVersionFile func() bool
-		switch tc.Mismatch {
-		case true:
-			matchesVersionFile = func() bool {
-				return false
-			}
-		default:
-			matchesVersionFile = func() bool {
-				return true
-			}
-		}
-		args := &languageArgs{
-			commands: []*cmd{
-				{
-					executable: "unicorn",
-					args:       []string{"--version"},
-					regex:      "(?P<version>.*)",
-				},
-			},
-			extensions:         []string{uni, corn},
-			enabledExtensions:  []string{uni, corn},
-			enabledCommands:    []string{"unicorn"},
-			version:            universion,
-			properties:         props,
-			matchesVersionFile: matchesVersionFile,
-		}
-		lang := bootStrapLanguageTest(args)
-		assert.True(t, lang.enabled(), tc.Case)
-		assert.Equal(t, universion, lang.string(), tc.Case)
-		if tc.ColorBackground {
-			assert.Equal(t, tc.ExpectedColor, lang.props[BackgroundOverride], tc.Case)
-			return
-		}
-		assert.Equal(t, tc.ExpectedColor, lang.props.getColor(ForegroundOverride, ""), tc.Case)
-	}
-}
diff --git a/src/segment_node_test.go b/src/segment_node_test.go
index ecc183f3..239c509b 100644
--- a/src/segment_node_test.go
+++ b/src/segment_node_test.go
@@ -75,6 +75,6 @@ func TestNodeInContext(t *testing.T) {
 			},
 		}
 		node.loadContext()
-		assert.Equal(t, tc.ExpectedString, node.packageManagerIcon, tc.Case)
+		assert.Equal(t, tc.ExpectedString, node.PackageManagerIcon, tc.Case)
 	}
 }
diff --git a/src/segment_python_test.go b/src/segment_python_test.go
index e93992f7..3aab1dcd 100644
--- a/src/segment_python_test.go
+++ b/src/segment_python_test.go
@@ -6,25 +6,38 @@ import (
 	"github.com/alecthomas/assert"
 )
 
-func TestPythonVirtualEnv(t *testing.T) {
+func TestPythonTemplate(t *testing.T) {
 	cases := []struct {
-		Case                string
-		Expected            string
-		ExpectedDisabled    bool
-		VirtualEnvName      string
-		CondaEnvName        string
-		CondaDefaultEnvName string
-		PyEnvName           string
-		DisplayVersion      bool
-		DisplayDefault      bool
+		Case             string
+		Expected         string
+		ExpectedDisabled bool
+		Template         string
+		VirtualEnvName   string
+		FetchVersion     bool
 	}{
-		{Case: "VENV", Expected: "VENV", VirtualEnvName: "VENV"},
-		{Case: "CONDA", Expected: "CONDA", CondaEnvName: "CONDA"},
-		{Case: "CONDA default", Expected: "CONDA", CondaDefaultEnvName: "CONDA"},
-		{Case: "Display Base", Expected: "base", CondaDefaultEnvName: "base", DisplayDefault: true},
-		{Case: "Hide base", Expected: "", CondaDefaultEnvName: "base", ExpectedDisabled: true},
-		{Case: "PYENV", Expected: "PYENV", PyEnvName: "PYENV"},
-		{Case: "PYENV Version", Expected: "PYENV 3.8.4", PyEnvName: "PYENV", DisplayVersion: true},
+		{Case: "No virtual env present", FetchVersion: true, Expected: "3.8.4", Template: "{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Full }}"},
+		{Case: "Virtual env present", FetchVersion: true, Expected: "VENV 3.8.4", VirtualEnvName: "VENV", Template: "{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Full }}"},
+		{
+			Case:           "Virtual env major and minor dot",
+			FetchVersion:   true,
+			Expected:       "VENV 3.8",
+			VirtualEnvName: "VENV",
+			Template:       "{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Major }}.{{ .Minor }}",
+		},
+		{
+			Case:           "Virtual env hide on default",
+			FetchVersion:   true,
+			Expected:       "3.8",
+			VirtualEnvName: "default",
+			Template:       "{{ if ne .Venv \"default\" }}{{ .Venv }} {{ end }}{{ .Major }}.{{ .Minor }}",
+		},
+		{
+			Case:           "Virtual env show non default",
+			FetchVersion:   true,
+			Expected:       "billy 3.8",
+			VirtualEnvName: "billy",
+			Template:       "{{ if ne .Venv \"default\" }}{{ .Venv }} {{ end }}{{ .Major }}.{{ .Minor }}",
+		},
 	}
 
 	for _, tc := range cases {
@@ -33,16 +46,16 @@ func TestPythonVirtualEnv(t *testing.T) {
 		env.On("runCommand", "python", []string{"--version"}).Return("Python 3.8.4", nil)
 		env.On("hasFiles", "*.py").Return(true)
 		env.On("getenv", "VIRTUAL_ENV").Return(tc.VirtualEnvName)
-		env.On("getenv", "CONDA_ENV_PATH").Return(tc.CondaEnvName)
-		env.On("getenv", "CONDA_DEFAULT_ENV").Return(tc.CondaDefaultEnvName)
-		env.On("getenv", "PYENV_VERSION").Return(tc.PyEnvName)
+		env.On("getenv", "CONDA_ENV_PATH").Return(tc.VirtualEnvName)
+		env.On("getenv", "CONDA_DEFAULT_ENV").Return(tc.VirtualEnvName)
+		env.On("getenv", "PYENV_VERSION").Return(tc.VirtualEnvName)
 		env.On("getPathSeperator", nil).Return("")
 		env.On("getcwd", nil).Return("/usr/home/project")
 		env.On("homeDir", nil).Return("/usr/home")
 		var props properties = map[Property]interface{}{
-			DisplayVersion:    tc.DisplayVersion,
-			DisplayVirtualEnv: true,
-			DisplayDefault:    tc.DisplayDefault,
+			FetchVersion:    tc.FetchVersion,
+			SegmentTemplate: tc.Template,
+			DisplayMode:     DisplayModeAlways,
 		}
 		python := &python{}
 		python.init(props, env)
diff --git a/src/segment_ruby_test.go b/src/segment_ruby_test.go
index cdcabd0e..ebcd9043 100644
--- a/src/segment_ruby_test.go
+++ b/src/segment_ruby_test.go
@@ -21,19 +21,19 @@ func TestRuby(t *testing.T) {
 		HasRubyFiles    bool
 		HasRakeFile     bool
 		HasGemFile      bool
-		DisplayVersion  bool
+		FetchVersion    bool
 	}{
 		{Case: "No files", ExpectedString: "", ExpectedEnabled: false},
-		{Case: "Ruby files", ExpectedString: "", ExpectedEnabled: true, DisplayVersion: false, HasRubyFiles: true},
-		{Case: "Rakefile", ExpectedString: "", ExpectedEnabled: true, DisplayVersion: false, HasRakeFile: true},
-		{Case: "Gemfile", ExpectedString: "", ExpectedEnabled: true, DisplayVersion: false, HasGemFile: true},
-		{Case: "Gemfile with version", ExpectedString: "", ExpectedEnabled: true, DisplayVersion: true, HasGemFile: true},
-		{Case: "No files with version", ExpectedString: "", ExpectedEnabled: false, DisplayVersion: true},
+		{Case: "Ruby files", ExpectedString: "", ExpectedEnabled: true, FetchVersion: false, HasRubyFiles: true},
+		{Case: "Rakefile", ExpectedString: "", ExpectedEnabled: true, FetchVersion: false, HasRakeFile: true},
+		{Case: "Gemfile", ExpectedString: "", ExpectedEnabled: true, FetchVersion: false, HasGemFile: true},
+		{Case: "Gemfile with version", ExpectedString: "", ExpectedEnabled: true, FetchVersion: true, HasGemFile: true},
+		{Case: "No files with version", ExpectedString: "", ExpectedEnabled: false, FetchVersion: true},
 		{
 			Case:            "Version with chruby",
 			ExpectedString:  "ruby-2.6.3",
 			ExpectedEnabled: true,
-			DisplayVersion:  true,
+			FetchVersion:    true,
 			HasRubyFiles:    true,
 			HasChruby:       true,
 			Version: ` * ruby-2.6.3
@@ -45,7 +45,7 @@ func TestRuby(t *testing.T) {
 			Case:            "Version with chruby line 2",
 			ExpectedString:  "ruby-1.9.3-p392",
 			ExpectedEnabled: true,
-			DisplayVersion:  true,
+			FetchVersion:    true,
 			HasRubyFiles:    true,
 			HasChruby:       true,
 			Version: ` ruby-2.6.3
@@ -57,7 +57,7 @@ func TestRuby(t *testing.T) {
 			Case:            "Version with asdf",
 			ExpectedString:  "2.6.3",
 			ExpectedEnabled: true,
-			DisplayVersion:  true,
+			FetchVersion:    true,
 			HasRubyFiles:    true,
 			HasAsdf:         true,
 			Version:         "ruby            2.6.3           /Users/jan/Projects/oh-my-posh/.tool-versions",
@@ -66,7 +66,7 @@ func TestRuby(t *testing.T) {
 			Case:            "Version with asdf not set",
 			ExpectedString:  "",
 			ExpectedEnabled: true,
-			DisplayVersion:  true,
+			FetchVersion:    true,
 			HasRubyFiles:    true,
 			HasAsdf:         true,
 			Version:         "ruby            ______          No version set. Run \"asdf <global|shell|local> ruby <version>\"",
@@ -75,7 +75,7 @@ func TestRuby(t *testing.T) {
 			Case:            "Version with ruby",
 			ExpectedString:  "2.6.3",
 			ExpectedEnabled: true,
-			DisplayVersion:  true,
+			FetchVersion:    true,
 			HasRubyFiles:    true,
 			HasRuby:         true,
 			Version:         "ruby  2.6.3 (2019-04-16 revision 67580) [universal.x86_64-darwin20]",
@@ -99,7 +99,7 @@ func TestRuby(t *testing.T) {
 		env.On("getcwd", nil).Return("/usr/home/project")
 		env.On("homeDir", nil).Return("/usr/home")
 		var props properties = map[Property]interface{}{
-			DisplayVersion: tc.DisplayVersion,
+			FetchVersion: tc.FetchVersion,
 		}
 		ruby := &ruby{}
 		ruby.init(props, env)
diff --git a/src/test/jandedobbeleer-palette.omp.json b/src/test/jandedobbeleer-palette.omp.json
index d14ca722..1da52e50 100644
--- a/src/test/jandedobbeleer-palette.omp.json
+++ b/src/test/jandedobbeleer-palette.omp.json
@@ -122,9 +122,9 @@
           "background": "p:python",
           "properties": {
             "prefix": " \uE235 ",
-            "display_version": true,
             "display_mode": "files",
-            "display_virtual_env": false
+            "fetch_virtual_env": false,
+            "template": "{{ .Full }}"
           }
         },
         {
diff --git a/src/test/jandedobbeleer.omp.json b/src/test/jandedobbeleer.omp.json
index c6719220..196894b8 100644
--- a/src/test/jandedobbeleer.omp.json
+++ b/src/test/jandedobbeleer.omp.json
@@ -91,9 +91,9 @@
           "background": "#FFDE57",
           "properties": {
             "prefix": " \uE235 ",
-            "display_version": true,
             "display_mode": "files",
-            "display_virtual_env": false
+            "fetch_virtual_env": false,
+            "template": "{{ .Full }}"
           }
         },
         {