Repository: JanDeDobbeleer/oh-my-posh
Pull request #1849 (2022-03-01 09:11:10)

feat(kotlin): adds kotlin segment
### 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

Adds a Kotlin language segment. Displays the currently active Kotlin version.

![image](https://user-images.githubusercontent.com/85419773/156139096-ea406a9f-24b1-45d9-9c06-e7f35328c238.png)

Prettier also fixed various missing spaces in the schema

Kind regards,
Jed

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/docs/docs/segment-kotlin.md b/docs/docs/segment-kotlin.md
new file mode 100644
index 00000000..4bc2bd76
--- /dev/null
+++ b/docs/docs/segment-kotlin.md
@@ -0,0 +1,57 @@
+---
+id: kotlin
+title: Kotlin
+sidebar_label: Kotlin
+---
+
+## What
+
+Display the currently active [Kotlin][kotlin] version.
+
+## Sample Configuration
+
+```json
+{
+  "type": "kotlin",
+  "style": "powerline",
+  "powerline_symbol": "\uE0B0",
+  "foreground": "#ffffff",
+  "background": "#906cff",
+  "properties": {
+    "template": " \ufa05 {{ .Full }} "
+  }
+}
+```
+
+## Properties
+
+- home_enabled: `boolean` - display the segment in the HOME folder or not - defaults to `false`
+- fetch_version: `boolean` - display the kotlin 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 `*.kt`, `*.kts` and `*.ktm` files are present (default)
+
+## Template ([info][templates])
+
+:::note default template
+
+``` template
+{{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }}
+```
+
+:::
+
+### Properties
+
+- `.Full`: `string` - the full version
+- `.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
+
+[templates]: /docs/config-templates
+[kotlin]: https://kotlinlang.org/
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 5d1901b1..d4cbe48a 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -59,6 +59,7 @@ module.exports = {
         "ipify",
         "java",
         "julia",
+        "kotlin",
         "kubectl",
         "nbgv",
         "nightscout",
diff --git a/src/engine/segment.go b/src/engine/segment.go
index bdfc1494..d7314577 100644
--- a/src/engine/segment.go
+++ b/src/engine/segment.go
@@ -158,6 +158,8 @@ const (
 	CF SegmentType = "cf"
 	// Cloud Foundry logged in target
 	CFTARGET SegmentType = "cftarget"
+	// KOTLIN writes the active kotlin version
+	KOTLIN SegmentType = "kotlin"
 )
 
 func (segment *Segment) shouldIncludeFolder() bool {
@@ -284,6 +286,7 @@ func (segment *Segment) mapSegmentWithWriter(env environment.Environment) error
 		UI5TOOLING:    &segments.UI5Tooling{},
 		CF:            &segments.Cf{},
 		CFTARGET:      &segments.CfTarget{},
+		KOTLIN:        &segments.Kotlin{},
 	}
 	if segment.Properties == nil {
 		segment.Properties = make(properties.Map)
diff --git a/src/segments/kotlin.go b/src/segments/kotlin.go
new file mode 100644
index 00000000..6cb6501f
--- /dev/null
+++ b/src/segments/kotlin.go
@@ -0,0 +1,34 @@
+package segments
+
+import (
+	"oh-my-posh/environment"
+	"oh-my-posh/properties"
+)
+
+type Kotlin struct {
+	language
+}
+
+func (k *Kotlin) Template() string {
+	return languageTemplate
+}
+
+func (k *Kotlin) Init(props properties.Properties, env environment.Environment) {
+	k.language = language{
+		env:        env,
+		props:      props,
+		extensions: []string{"*.kt", "*.kts", "*.ktm"},
+		commands: []*cmd{
+			{
+				executable: "kotlin",
+				args:       []string{"-version"},
+				regex:      `Kotlin version (?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+)))`,
+			},
+		},
+		versionURLTemplate: "https://github.com/JetBrains/kotlin/releases/tag/v{{ .Full }})",
+	}
+}
+
+func (k *Kotlin) Enabled() bool {
+	return k.language.Enabled()
+}
diff --git a/themes/schema.json b/themes/schema.json
index ff17194b..f0a96e42 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -196,7 +196,8 @@
             "plastic",
             "ipify",
             "haskell",
-            "ui5tooling"
+            "ui5tooling",
+            "kotlin"
           ]
         },
         "style": {
@@ -1150,7 +1151,7 @@
                     "description": "Replace known locations in the path with the replacements before applying the style.",
                     "default": true
                   },
-                  "mixed_threshold":{
+                  "mixed_threshold": {
                     "type": "integer",
                     "title": "Mixed threshold",
                     "description": "The maximum length of a path segment that will be displayed when using mixed style.",
@@ -1665,10 +1666,10 @@
                     "default": ""
                   },
                   "fallback": {
-                    "type":"string",
-                    "title":"Fallback value",
-                    "description":"Value to display if registry value cannot be retrieved",
-                    "default":""
+                    "type": "string",
+                    "title": "Fallback value",
+                    "description": "Value to display if registry value cannot be retrieved",
+                    "default": ""
                   }
                 }
               }
@@ -1747,22 +1748,22 @@
                     "default": ""
                   },
                   "api_key": {
-                    "type":"string",
-                    "title":"Brewfather API Key (required)",
-                    "description":"Provided by Brewfather's Generate API Key settings option",
-                    "default":""
+                    "type": "string",
+                    "title": "Brewfather API Key (required)",
+                    "description": "Provided by Brewfather's Generate API Key settings option",
+                    "default": ""
                   },
                   "batch_id": {
-                    "type":"string",
-                    "title":"ID of the batch in Brewfather (required)",
-                    "description":"At the end of the URL when viewing the batch on the Brewfather site",
-                    "default":""
+                    "type": "string",
+                    "title": "ID of the batch in Brewfather (required)",
+                    "description": "At the end of the URL when viewing the batch on the Brewfather site",
+                    "default": ""
                   },
                   "day_icon": {
-                    "type":"string",
-                    "title":"Icon to use to indicate days",
-                    "description":"Appended to a number to indicate days, e.g. 25d",
-                    "default":"d"
+                    "type": "string",
+                    "title": "Icon to use to indicate days",
+                    "description": "Appended to a number to indicate days, e.g. 25d",
+                    "default": "d"
                   },
                   "http_timeout": {
                     "$ref": "#/definitions/http_timeout"
@@ -1774,82 +1775,82 @@
                     "default": 5
                   },
                   "doubleup_icon": {
-                    "type":"string",
-                    "title":"Temperature trend icon, very high positive change",
-                    "description":"Delta between this and prior temperature reading is very high (> 4C by default), available intemplate as .TemperatureTrend",
-                    "default":"↑↑"
+                    "type": "string",
+                    "title": "Temperature trend icon, very high positive change",
+                    "description": "Delta between this and prior temperature reading is very high (> 4C by default), available intemplate as .TemperatureTrend",
+                    "default": "↑↑"
                   },
                   "singleup_icon": {
-                    "type":"string",
-                    "title":"Temperature trend icon, high positive change",
-                    "description":"Delta between this and prior temperature reading is high (2C < delta < 4C by default), available intemplate as .TemperatureTrend",
-                    "default":"↑"
+                    "type": "string",
+                    "title": "Temperature trend icon, high positive change",
+                    "description": "Delta between this and prior temperature reading is high (2C < delta < 4C by default), available intemplate as .TemperatureTrend",
+                    "default": "↑"
                   },
                   "fortyfiveup_icon": {
-                    "type":"string",
-                    "title":"Temperature trend icon, positive change",
-                    "description":"Delta between this and prior temperature reading is positive (0.5C < delta < 2C by default), available intemplate as .TemperatureTrend",
-                    "default":"↗"
+                    "type": "string",
+                    "title": "Temperature trend icon, positive change",
+                    "description": "Delta between this and prior temperature reading is positive (0.5C < delta < 2C by default), available intemplate as .TemperatureTrend",
+                    "default": "↗"
                   },
                   "flat_icon": {
-                    "type":"string",
-                    "title":"Temperature trend icon, flat/small change",
-                    "description":"Delta between this and prior temperature and this temperature reading (< +-0.5C change), available intemplate as .TemperatureTrend",
-                    "default":"→"
+                    "type": "string",
+                    "title": "Temperature trend icon, flat/small change",
+                    "description": "Delta between this and prior temperature and this temperature reading (< +-0.5C change), available intemplate as .TemperatureTrend",
+                    "default": "→"
                   },
                   "fortyfivedown_icon": {
-                    "type":"string",
-                    "title":"Temperature trend icon, v. negative change",
-                    "description":"Delta between this and prior temperature reading is negative (-0.5C > delta > -2C by default), available intemplate as .TemperatureTrend",
-                    "default":"↘"
+                    "type": "string",
+                    "title": "Temperature trend icon, v. negative change",
+                    "description": "Delta between this and prior temperature reading is negative (-0.5C > delta > -2C by default), available intemplate as .TemperatureTrend",
+                    "default": "↘"
                   },
                   "singledown_icon": {
-                    "type":"string",
-                    "title":"Temperature trend icon, high negative change",
-                    "description":"Delta between this and prior temperature reading is large negative (-2C > delta > -4C by default), available intemplate as .TemperatureTrend",
-                    "default":"↓"
+                    "type": "string",
+                    "title": "Temperature trend icon, high negative change",
+                    "description": "Delta between this and prior temperature reading is large negative (-2C > delta > -4C by default), available intemplate as .TemperatureTrend",
+                    "default": "↓"
                   },
                   "doubledown_icon": {
-                    "type":"string",
-                    "title":"Temperature trend icon, very high negative change",
-                    "description":"Delta between this and prior temperature reading is very large negative (> -4C by default), available intemplate as .TemperatureTrend",
-                    "default":"↓↓"
+                    "type": "string",
+                    "title": "Temperature trend icon, very high negative change",
+                    "description": "Delta between this and prior temperature reading is very large negative (> -4C by default), available intemplate as .TemperatureTrend",
+                    "default": "↓↓"
                   },
                   "planning_status_icon": {
-                    "type":"string",
-                    "title":"Icon for batch in planning",
-                    "description":"Available in template as .StatusIcon",
-                    "default":"\uF8EA"
+                    "type": "string",
+                    "title": "Icon for batch in planning",
+                    "description": "Available in template as .StatusIcon",
+                    "default": "\uF8EA"
                   },
                   "brewing_status_icon": {
-                    "type":"string",
-                    "title":"Icon for batch being brewed",
-                    "description":"Available in template as .StatusIcon",
-                    "default":"\uF7DE"
+                    "type": "string",
+                    "title": "Icon for batch being brewed",
+                    "description": "Available in template as .StatusIcon",
+                    "default": "\uF7DE"
                   },
                   "fermenting_status_icon": {
-                    "type":"string",
-                    "title":"Icon for batch fermenting",
-                    "description":"Available in template as .StatusIcon",
-                    "default":"\uF499"
+                    "type": "string",
+                    "title": "Icon for batch fermenting",
+                    "description": "Available in template as .StatusIcon",
+                    "default": "\uF499"
                   },
                   "conditioning_status_icon": {
-                    "type":"string",
-                    "title":"Icon for batch conditioning",
-                    "description":"Available in template as .StatusIcon",
-                    "default":"\uE372"
+                    "type": "string",
+                    "title": "Icon for batch conditioning",
+                    "description": "Available in template as .StatusIcon",
+                    "default": "\uE372"
                   },
                   "completed_status_icon": {
-                    "type":"string",
-                    "title":"Icon for completed batch",
-                    "description":"Available in template as .StatusIcon",
-                    "default":"\uF7A5"
+                    "type": "string",
+                    "title": "Icon for completed batch",
+                    "description": "Available in template as .StatusIcon",
+                    "default": "\uF7A5"
                   },
                   "archived_status_icon": {
-                    "type":"string",
-                    "title":"Icon for archived batch",
-                    "description":"Available in template as .StatusIcon",
-                    "default":"\uF187"
+                    "type": "string",
+                    "title": "Icon for archived batch",
+                    "description": "Available in template as .StatusIcon",
+                    "default": "\uF187"
                   }
                 }
               }
@@ -1983,6 +1984,32 @@
             "title": "Clound Foundry Target segment",
             "description": "https://ohmyposh.dev/docs/cftarget"
           }
+        },
+        {
+          "if": {
+            "properties": {
+              "type": { "const": "kotlin" }
+            }
+          },
+          "then": {
+            "title": "Kotlin Segment",
+            "description": "https://ohmyposh.dev/docs/kotlin",
+            "properties": {
+              "properties": {
+                "properties": {
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
+                  },
+                  "display_mode": {
+                    "$ref": "#/definitions/display_mode"
+                  },
+                  "missing_command_text": {
+                    "$ref": "#/definitions/missing_command_text"
+                  }
+                }
+              }
+            }
+          }
         }
       ]
     }


Test patch:
diff --git a/src/segments/kotlin_test.go b/src/segments/kotlin_test.go
new file mode 100644
index 00000000..5ff98752
--- /dev/null
+++ b/src/segments/kotlin_test.go
@@ -0,0 +1,32 @@
+package segments
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestKotlin(t *testing.T) {
+	cases := []struct {
+		Case           string
+		ExpectedString string
+		Version        string
+	}{
+		{Case: "Kotlin 1.6.10", ExpectedString: "1.6.10", Version: "Kotlin version 1.6.10-release-923 (JRE 17.0.2+0)"},
+		{Case: "Kotlin 1.6.0", ExpectedString: "1.6.0", Version: "Kotlin version 1.6.0-release-915 (JRE 17.0.2+0)"},
+	}
+	for _, tc := range cases {
+		params := &mockedLanguageParams{
+			cmd:           "kotlin",
+			versionParam:  "-version",
+			versionOutput: tc.Version,
+			extension:     "*.kt",
+		}
+		env, props := getMockedLanguageEnv(params)
+		k := &Kotlin{}
+		k.Init(props, env)
+		assert.True(t, k.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case))
+		assert.Equal(t, tc.ExpectedString, renderTemplate(env, k.Template(), k), fmt.Sprintf("Failed in case: %s", tc.Case))
+	}
+}