Repository: JanDeDobbeleer/oh-my-posh
Pull request #5031 (2024-06-09 09:41:13)

feat(segment): add `bun`
### 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

Adds a segment for [`bun`](https://bun.sh/).

<!---

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.

-->

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary


Joxtacy: I tried to add 🥟 as the emoji using the code point `\0001f95f`, but it doesn't render correctly in the docs.
It works as expected in my terminal (alacritty) when I add it in my template `template = "[\U0001f95f {{ .Full }}]"` though.
Any input on how I should proceed here?
JanDeDobbeleer: @Joxtacy maybe the docs Nerd Font isn't V3 yet? 
Joxtacy: > @Joxtacy maybe the docs Nerd Font isn't V3 yet?

It seems like it's the `JSON.stringify`, `YAML.stringify`, `TOML.stringify` functions that aren't replacing the code point correctly. I could create a makeshift replacer function for both JSON and YAML to manually call `String.fromCodePoint("0x0001f95f")` and that then renders the emoji correctly.

Not sure what to do about that. Maybe I can just find another icon that plays more nicely. 🙃
Joxtacy: > @Joxtacy what if you paste in the actual emoji rather than the code point?

That works just fine. I thought you wanted to have the code points, but maybe I misunderstood the contribution docs.
JanDeDobbeleer: > I thought you wanted to have the code points

@Joxtacy back in the day, check, but now as they're being rendered, this isn't required anymore.
Joxtacy: I forgot to update my test... 🙃

Patch:
diff --git a/src/engine/segment.go b/src/engine/segment.go
index aa26c35c..ec01e092 100644
--- a/src/engine/segment.go
+++ b/src/engine/segment.go
@@ -111,6 +111,8 @@ const (
 	BREWFATHER SegmentType = "brewfather"
 	// Buf segment writes the active buf version
 	BUF SegmentType = "buf"
+	// BUN writes the active bun version
+	BUN SegmentType = "bun"
 	// CARBONINTENSITY writes the actual and forecast carbon intensity in gCO2/kWh
 	CARBONINTENSITY SegmentType = "carbonintensity"
 	// cds (SAP CAP) version
@@ -281,6 +283,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
 	BAZEL:           func() SegmentWriter { return &segments.Bazel{} },
 	BREWFATHER:      func() SegmentWriter { return &segments.Brewfather{} },
 	BUF:             func() SegmentWriter { return &segments.Buf{} },
+	BUN:             func() SegmentWriter { return &segments.Bun{} },
 	CARBONINTENSITY: func() SegmentWriter { return &segments.CarbonIntensity{} },
 	CDS:             func() SegmentWriter { return &segments.Cds{} },
 	CF:              func() SegmentWriter { return &segments.Cf{} },
diff --git a/src/segments/bun.go b/src/segments/bun.go
new file mode 100644
index 00000000..684cff1a
--- /dev/null
+++ b/src/segments/bun.go
@@ -0,0 +1,34 @@
+package segments
+
+import (
+	"github.com/jandedobbeleer/oh-my-posh/src/platform"
+	"github.com/jandedobbeleer/oh-my-posh/src/properties"
+)
+
+type Bun struct {
+	language
+}
+
+func (b *Bun) Template() string {
+	return languageTemplate
+}
+
+func (b *Bun) Init(props properties.Properties, env platform.Environment) {
+	b.language = language{
+		env:        env,
+		props:      props,
+		extensions: []string{"bun.lockb"},
+		commands: []*cmd{
+			{
+				executable: "bun",
+				args:       []string{"--version"},
+				regex:      `(?:(?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+))))`,
+			},
+		},
+		versionURLTemplate: "https://github.com/oven-sh/bun/releases/tag/bun-v{{.Full}}",
+	}
+}
+
+func (b *Bun) Enabled() bool {
+	return b.language.Enabled()
+}
diff --git a/themes/schema.json b/themes/schema.json
index 26ff61f2..431758de 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -297,6 +297,7 @@
             "bazel",
             "brewfather",
             "buf",
+            "bun",
             "carbonintensity",
             "command",
             "connection",
@@ -824,6 +825,57 @@
             }
           }
         },
+        {
+          "if": {
+            "properties": {
+              "type": {
+                "const": "bun"
+              }
+            }
+          },
+          "then": {
+            "title": "Bun CLI Segment",
+            "description": "https://ohmyposh.dev/docs/segments/bun",
+            "properties": {
+              "properties": {
+                "properties": {
+                  "home_enabled": {
+                    "$ref": "#/definitions/home_enabled"
+                  },
+                  "fetch_version": {
+                    "$ref": "#/definitions/fetch_version"
+                  },
+                  "missing_command_text": {
+                    "$ref": "#/definitions/missing_command_text"
+                  },
+                  "display_mode": {
+                    "$ref": "#/definitions/display_mode"
+                  },
+                  "version_url_template": {
+                    "$ref": "#/definitions/version_url_template"
+                  },
+                  "cache_version": {
+                    "$ref": "#/definitions/cache_version"
+                  },
+                  "extensions": {
+                    "type": "array",
+                    "title": "Extensions",
+                    "description": "The extensions to look for when determining if a folder is a Bun workspace",
+                    "default": [
+                      "bun.lockb"
+                    ],
+                    "items": {
+                      "type": "string"
+                    }
+                  },
+                  "folders": {
+                    "$ref": "#/definitions/folders"
+                  }
+                }
+              }
+            }
+          }
+        },
         {
           "if": {
             "properties": {
diff --git a/website/docs/segments/bun.mdx b/website/docs/segments/bun.mdx
new file mode 100644
index 00000000..8ec9c8cf
--- /dev/null
+++ b/website/docs/segments/bun.mdx
@@ -0,0 +1,60 @@
+---
+id: bun
+title: Bun
+sidebar_label: Bun
+---
+
+## What
+
+Display the currently active [Bun CLI][bun-docs] version.
+
+## Sample Configuration
+
+import Config from "@site/src/components/Config.js";
+
+<Config
+  data={{
+    type: "bun",
+    style: "plain",
+    foreground: "#3C82F6",
+    template: " 🥟 {{ .Full }} ",
+  }}
+/>
+
+## Properties
+
+| Name                   | Type       | Default                 | Description                                                                                                                                                                                                                          |
+| ---------------------- | :--------: | :---------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `home_enabled`         | `boolean`  | `false`                 | display the segment in the HOME folder or not                                                                                                                                                                                        |
+| `fetch_version`        | `boolean`  | `true`                  | fetch the active version or not; useful if all you need is an icon indicating `bun`                                                                                                                                                 |
+| `missing_command_text` | `string`   |                         | text to display when the command is missing                                                                                                                                                                                          |
+| `display_mode`         | `string`   | `context`               | <ul><li>`always`: the segment is always displayed</li><li>`files`: the segment is only displayed when file `extensions` listed are present</li><li>`context`: displays the segment when the environment or files is active</li></ul> |
+| `version_url_template` | `string`   |                         | a go [text/template][go-text-template] [template][templates] that creates the URL of the version info / release notes                                                                                                                |
+| `extensions`           | `[]string` | `bun.lockb`             | allows to override the default list of file extensions to validate                                                                                                                                                                   |
+| `folders`              | `[]string` |                         | allows to override the list of folder names to validate                                                                                                                                                                              |
+| `cache_version`        | `boolean`  | `false`                 | cache the executable's version or not                                                                                                                                                                                                |
+
+## 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                                       |
+| `.URL`   | `string` | URL of the version info / release notes            |
+| `.Error` | `string` | error encountered when fetching the version string |
+
+[go-text-template]: https://golang.org/pkg/text/template/
+[templates]: /docs/configuration/templates
+[bun-docs]: https://bun.sh/
diff --git a/website/sidebars.js b/website/sidebars.js
index bad40203..1f98ba0f 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -60,6 +60,7 @@ module.exports = {
         "segments/bazel",
         "segments/brewfather",
         "segments/buf",
+        "segments/bun",
         "segments/carbonintensity",
         "segments/cds",
         "segments/cf",


Test patch:
diff --git a/src/segments/bun_test.go b/src/segments/bun_test.go
new file mode 100644
index 00000000..5ffa7802
--- /dev/null
+++ b/src/segments/bun_test.go
@@ -0,0 +1,31 @@
+package segments
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestBun(t *testing.T) {
+	cases := []struct {
+		Case           string
+		ExpectedString string
+		Version        string
+	}{
+		{Case: "Bun 1.1.8", ExpectedString: "1.1.8", Version: "1.1.8"},
+	}
+	for _, tc := range cases {
+		params := &mockedLanguageParams{
+			cmd:           "bun",
+			versionParam:  "--version",
+			versionOutput: tc.Version,
+			extension:     "bun.lockb",
+		}
+		env, props := getMockedLanguageEnv(params)
+		b := &Bun{}
+		b.Init(props, env)
+		assert.True(t, b.Enabled(), fmt.Sprintf("Failed in case: %s", tc.Case))
+		assert.Equal(t, tc.ExpectedString, renderTemplate(env, b.Template(), b), fmt.Sprintf("Failed in case: %s", tc.Case))
+	}
+}