Repository: JanDeDobbeleer/oh-my-posh
Pull request #4626 (2024-01-15 13:36:51)

feat: ocaml segment
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md].
- [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

Support ocaml language.   
Enabled when *.ml, *.mli, dune, dune-project or dune-workspace files are present.

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/src/engine/segment.go b/src/engine/segment.go
index a2f4229a..13969281 100644
--- a/src/engine/segment.go
+++ b/src/engine/segment.go
@@ -186,6 +186,8 @@ const (
 	NPM SegmentType = "npm"
 	// NX writes which Nx version us currently active
 	NX SegmentType = "nx"
+	// OCAML writes the active Ocaml version
+	OCAML SegmentType = "ocaml"
 	// OS write os specific icon
 	OS SegmentType = "os"
 	// OWM writes the weather coming from openweatherdata
@@ -310,6 +312,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
 	NODE:            func() SegmentWriter { return &segments.Node{} },
 	NPM:             func() SegmentWriter { return &segments.Npm{} },
 	NX:              func() SegmentWriter { return &segments.Nx{} },
+	OCAML:           func() SegmentWriter { return &segments.OCaml{} },
 	OS:              func() SegmentWriter { return &segments.Os{} },
 	OWM:             func() SegmentWriter { return &segments.Owm{} },
 	PATH:            func() SegmentWriter { return &segments.Path{} },
diff --git a/src/segments/ocaml.go b/src/segments/ocaml.go
new file mode 100644
index 00000000..c0e293ef
--- /dev/null
+++ b/src/segments/ocaml.go
@@ -0,0 +1,33 @@
+package segments
+
+import (
+	"github.com/jandedobbeleer/oh-my-posh/src/platform"
+	"github.com/jandedobbeleer/oh-my-posh/src/properties"
+)
+
+type OCaml struct {
+	language
+}
+
+func (o *OCaml) Template() string {
+	return languageTemplate
+}
+
+func (o *OCaml) Init(props properties.Properties, env platform.Environment) {
+	o.language = language{
+		env:        env,
+		props:      props,
+		extensions: []string{"*.ml", "*.mli", "dune", "dune-project", "dune-workspace"},
+		commands: []*cmd{
+			{
+				executable: "ocaml",
+				args:       []string{"-version"},
+				regex:      `The OCaml toplevel, version (?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+))(-(?P<prerelease>[a-z]+))?)`,
+			},
+		},
+	}
+}
+
+func (o *OCaml) Enabled() bool {
+	return o.language.Enabled()
+}
diff --git a/themes/schema.json b/themes/schema.json
index cc630e16..d7642911 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -286,6 +286,7 @@
             "node",
             "npm",
             "nx",
+            "ocaml",
             "os",
             "owm",
             "path",
@@ -2578,6 +2579,37 @@
             }
           }
         },
+        {
+          "if": {
+            "properties": {
+              "type": {
+                "const": "ocaml"
+              }
+            }
+          },
+          "then": {
+            "title": "OCaml Segment",
+            "description": "https://ohmyposh.dev/docs/segments/ocaml",
+            "properties": {
+              "properties": {
+                "properties": {
+                  "home_enabled": {
+                    "$ref": "#/definitions/home_enabled"
+                  },
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
+                  },
+                  "display_mode": {
+                    "$ref": "#/definitions/display_mode"
+                  },
+                  "missing_command_text": {
+                    "$ref": "#/definitions/missing_command_text"
+                  }
+                }
+              }
+            }
+          }
+        },
         {
           "if": {
             "properties": {
diff --git a/website/docs/segments/ocaml.mdx b/website/docs/segments/ocaml.mdx
new file mode 100644
index 00000000..fa6c9aca
--- /dev/null
+++ b/website/docs/segments/ocaml.mdx
@@ -0,0 +1,53 @@
+---
+id: ocaml
+title: Ocaml
+sidebar_label: Ocaml
+---
+
+## What
+
+Display the currently active OCaml version.
+
+## Sample Configuration
+
+import Config from '@site/src/components/Config.js';
+
+<Config data={{
+  "type": "ocaml",
+  "style": "powerline",
+  "powerline_symbol": "\uE0B0",
+  "foreground": "#d08770",
+  "template": " \ue67a {{ .Full }} "
+}}/>
+
+## Properties
+
+| Name                   | Type      | Description                                                                                                                                                                          |
+| ---------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `home_enabled`         | `boolean` | display the segment in the HOME folder or not - defaults to `false`                                                                                                                  |
+| `fetch_version`        | `boolean` | display the ocaml version (`ocaml -version`) - defaults to `true`                                                                                                                    |
+| `missing_command_text` | `string`  | text to display when the command is missing - defaults to empty                                                                                                                      |
+| `display_mode`         | `string`  | <ul><li>`always`: the segment is always displayed</li><li>`files`: the segment is only displayed when `*.rs`, `Cargo.toml` or `Cargo.lock` files are present (**default**)</li></ul> |
+
+## Template ([info][templates])
+
+:::note default template
+
+```template
+{{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }}
+```
+
+:::
+
+### Properties
+
+| Name          | Type     | Description                                        |
+| ------------- | -------- | -------------------------------------------------- |
+| `.Full`       | `string` | the full version                                   |
+| `.Major`      | `string` | major number                                       |
+| `.Minor`      | `string` | minor number                                       |
+| `.Patch`      | `string` | patch number                                       |
+| `.Prerelease` | `string` | channel name                                       |
+| `.Error`      | `string` | error encountered when fetching the version string |
+
+[templates]: /docs/configuration/templates
diff --git a/website/sidebars.js b/website/sidebars.js
index ab659c58..bc5964cd 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -96,6 +96,7 @@ module.exports = {
         "segments/node",
         "segments/npm",
         "segments/nx",
+        "segments/ocaml",
         "segments/os",
         "segments/owm",
         "segments/path",


Test patch:
diff --git a/src/segments/ocaml_test.go b/src/segments/ocaml_test.go
new file mode 100644
index 00000000..73b2aaa6
--- /dev/null
+++ b/src/segments/ocaml_test.go
@@ -0,0 +1,33 @@
+package segments
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestOCaml(t *testing.T) {
+	cases := []struct {
+		Case           string
+		ExpectedString string
+		Version        string
+	}{
+		{Case: "OCaml 4.12.0", ExpectedString: "4.12.0", Version: "The OCaml toplevel, version 4.12.0"},
+		{Case: "OCaml 4.11.0", ExpectedString: "4.11.0", Version: "The OCaml toplevel, version 4.11.0"},
+		{Case: "OCaml 4.13.0", ExpectedString: "4.13.0", Version: "The OCaml toplevel, version 4.13.0"},
+	}
+	for _, tc := range cases {
+		params := &mockedLanguageParams{
+			cmd:           "ocaml",
+			versionParam:  "-version",
+			versionOutput: tc.Version,
+			extension:     "*.ml",
+		}
+		env, props := getMockedLanguageEnv(params)
+		o := &OCaml{}
+		o.Init(props, env)
+		assert.True(t, o.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case))
+		assert.Equal(t, tc.ExpectedString, renderTemplate(env, o.Template(), o), fmt.Sprintf("Failed in case: %s", tc.Case))
+	}
+}