Repository: JanDeDobbeleer/oh-my-posh
Pull request #1179 (2021-11-06 11:17:13)

feat(dotnet): template + semver regex
### Prerequisites

- [x] I have read and understood the `CONTRIBUTING` guide
- [x] The commit message follows the [conventional commits][cc] guidelines
- [ ] Tests for the changes have been added (for bug fixes/features)
- [ ] Docs have been added / updated (for bug fixes/features)

### Description

WIP  
New regex to extract rc part + template 

<!---

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


lnu: sample:
```
        {
          "background": "#512BD4", // .NET purple from https://dot.net
          "foreground": "#111111",
          "powerline_symbol": "",
          "properties": {
            "display_version": true,
            "prefix": " \uE77F ",
            "enable_hyperlink": true,
            "template": "{{.Major}}.{{.Minor}}.{{.Patch}}{{if .Prerelease}}.{{.Prerelease}}{{end}}"
          },
          "style": "powerline",
          "type": "dotnet"
        },
```
lnu: I'm not sure what's best: give full flexibility with templates for both text and hyperlink or have a predefined one. It means we have to update all language segments to have a predefined url in the template format.
lnu: > 1 typo, I would ship it like this for now. Well done.

let me update the doc

Patch:
diff --git a/docs/docs/segment-dotnet.md b/docs/docs/segment-dotnet.md
index 805bf79a..bc82bb74 100644
--- a/docs/docs/segment-dotnet.md
+++ b/docs/docs/segment-dotnet.md
@@ -36,3 +36,18 @@ Display the currently active .NET SDK version.
   or `*.fsproj` files are present (default)
 - unsupported_version_icon: `string` - text/icon that is displayed when the active .NET SDK version (e.g., one specified
   by `global.json`) is not installed/supported - defaults to `\uf071` (X in a rectangle box)
+- template: `string` - A go [text/template][go-text-template] template extended with [sprig][sprig] utilizing the
+properties below. Defaults does nothing(backward compatibility).
+- version_url_template: `string` - A go [text/template][go-text-template] template extended
+with [sprig][sprig] utilizing the properties below. Defaults does nothing(backward compatibility).
+
+### Template Properties
+
+- `.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
+
+[go-text-template]: https://golang.org/pkg/text/template/
+[sprig]: https://masterminds.github.io/sprig/
diff --git a/src/properties.go b/src/properties.go
index 1d8db454..b494d188 100644
--- a/src/properties.go
+++ b/src/properties.go
@@ -29,6 +29,8 @@ const (
 	AlwaysEnabled Property = "always_enabled"
 	// SegmentTemplate is the template to use to render the information
 	SegmentTemplate Property = "template"
+	// VersionURLTemplate is the template to use when building language segment hyperlink
+	VersionURLTemplate Property = "version_url_template"
 	// DisplayError to display when an error occurs or not
 	DisplayError Property = "display_error"
 	// DisplayDefault hides or shows the default
diff --git a/src/segment_dotnet.go b/src/segment_dotnet.go
index ed553010..a03ca9d1 100644
--- a/src/segment_dotnet.go
+++ b/src/segment_dotnet.go
@@ -30,7 +30,8 @@ func (d *dotnet) init(props *properties, env environmentInfo) {
 			{
 				executable: "dotnet",
 				args:       []string{"--version"},
-				regex:      `(?:(?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?:\d{2})(?P<patch>[0-9]{1}))))`,
+				regex: `(?P<version>((?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)` +
+					`(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?))`,
 			},
 		},
 		versionURLTemplate: "[%1s](https://github.com/dotnet/core/blob/master/release-notes/%[2]s.%[3]s/%[2]s.%[3]s.%[4]s/%[2]s.%[3]s.%[4]s.md)",
diff --git a/src/segment_language.go b/src/segment_language.go
index 41c96876..3f2b5a70 100644
--- a/src/segment_language.go
+++ b/src/segment_language.go
@@ -13,10 +13,12 @@ type inContext func() bool
 type matchesVersionFile func() bool
 
 type version struct {
-	full  string
-	major string
-	minor string
-	patch string
+	Full          string
+	Major         string
+	Minor         string
+	Patch         string
+	Prerelease    string
+	BuildMetadata string
 }
 
 type cmd struct {
@@ -32,16 +34,18 @@ func (c *cmd) parse(versionInfo string) error {
 		return errors.New("cannot parse version string")
 	}
 	c.version = &version{}
-	c.version.full = values["version"]
-	c.version.major = values["major"]
-	c.version.minor = values["minor"]
-	c.version.patch = values["patch"]
+	c.version.Full = values["version"]
+	c.version.Major = values["major"]
+	c.version.Minor = values["minor"]
+	c.version.Patch = values["patch"]
+	c.version.Prerelease = values["prerelease"]
+	c.version.BuildMetadata = values["buildmetadata"]
 	return nil
 }
 
-func (c *cmd) buildVersionURL(template string) string {
+func (c *cmd) buildVersionURL(text, template string) string {
 	if template == "" {
-		return c.version.full
+		return text
 	}
 	truncatingSprintf := func(str string, args ...interface{}) (string, error) {
 		n := strings.Count(str, "%s")
@@ -53,9 +57,9 @@ func (c *cmd) buildVersionURL(template string) string {
 		}
 		return fmt.Sprintf(str, args[:n]...), nil
 	}
-	version, err := truncatingSprintf(template, c.version.full, c.version.major, c.version.minor, c.version.patch)
+	version, err := truncatingSprintf(template, text, c.version.Major, c.version.Minor, c.version.Patch)
 	if err != nil {
-		return c.version.full
+		return text
 	}
 	return version
 }
@@ -110,13 +114,40 @@ func (l *language) string() string {
 		return ""
 	}
 
+	segmentTemplate := l.props.getString(SegmentTemplate, "{{.Full}}")
+	template := &textTemplate{
+		Template: segmentTemplate,
+		Context:  l.activeCommand.version,
+		Env:      l.env,
+	}
+	text, err := template.render()
+	if err != nil {
+		return err.Error()
+	}
+
 	if l.props.getBool(EnableHyperlink, false) {
-		return l.activeCommand.buildVersionURL(l.versionURLTemplate)
+		versionURLTemplate := l.props.getString(VersionURLTemplate, "")
+		// backward compatibility
+		if versionURLTemplate == "" {
+			text = l.activeCommand.buildVersionURL(text, l.versionURLTemplate)
+		} else {
+			template := &textTemplate{
+				Template: versionURLTemplate,
+				Context:  l.activeCommand.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 l.activeCommand.version.full
+	return text
 }
 
 func (l *language) enabled() bool {
diff --git a/src/segment_node.go b/src/segment_node.go
index 5980bcfb..79f76e55 100644
--- a/src/segment_node.go
+++ b/src/segment_node.go
@@ -61,5 +61,5 @@ func (n *node) matchesVersionFile() bool {
 	if len(fileVersion) == 0 {
 		return true
 	}
-	return fileVersion == n.language.activeCommand.version.full
+	return fileVersion == n.language.activeCommand.version.Full
 }


Test patch:
diff --git a/src/segment_node_test.go b/src/segment_node_test.go
index 8cb5b593..637055a4 100644
--- a/src/segment_node_test.go
+++ b/src/segment_node_test.go
@@ -26,7 +26,7 @@ func TestNodeMatchesVersionFile(t *testing.T) {
 				env: env,
 				activeCommand: &cmd{
 					version: &version{
-						full: tc.Version,
+						Full: tc.Version,
 					},
 				},
 			},