diff --git a/docs/docs/segment-cds.md b/docs/docs/segment-cds.md new file mode 100644 index 00000000..fc1db967 --- /dev/null +++ b/docs/docs/segment-cds.md @@ -0,0 +1,59 @@ +--- +id: cds +title: CDS (SAP CAP) +sidebar_label: CDS +--- + +## What + +Display the active [CDS CLI][sap-cap-cds] version. + +## Sample Configuration + +```json +{ + "background": "#a7cae1", + "foreground": "#100e23", + "powerline_symbol": "\ue0b0", + "properties": { + "template": " \ue311 cds {{ .Full }} " + }, + "style": "powerline", + "type": "cds" +} +``` + +## Properties + +- home_enabled: `boolean` - display the segment in the HOME folder or not - defaults to `false` +- 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 cds 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 displayed when `.cdsrc.json`, `.cdsrc-private` or `*.cds` file is present + - `context`: (default) the segment is displayed when conditions from `files` mode are fulfilled or + `package.json` file is present and `@sap/cds` is in `dependencies` section + +## Template ([info][templates]) + +:::note default template + +```template +{{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} +``` + +::: + +## Template 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 +- `.HasDependency`: `bool` - a flag if `@sap/cds` was found in `package.json` + +[templates]: /docs/config-templates +[sap-cap-cds]: https://cap.cloud.sap/docs/tools/#command-line-interface-cli diff --git a/docs/sidebars.js b/docs/sidebars.js index 0f431578..0f9c8c8b 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -44,6 +44,7 @@ module.exports = { "azfunc", "battery", "brewfather", + "cds", "command", "crystal", "cf", diff --git a/src/engine/segment.go b/src/engine/segment.go index de35a834..2903cf88 100644 --- a/src/engine/segment.go +++ b/src/engine/segment.go @@ -162,6 +162,8 @@ const ( KOTLIN SegmentType = "kotlin" // SWIFT writes the active swift version SWIFT SegmentType = "swift" + // cds (SAP CAP) version + CDS SegmentType = "cds" ) func (segment *Segment) shouldIncludeFolder() bool { @@ -290,6 +292,7 @@ func (segment *Segment) mapSegmentWithWriter(env environment.Environment) error CFTARGET: &segments.CfTarget{}, KOTLIN: &segments.Kotlin{}, SWIFT: &segments.Swift{}, + CDS: &segments.Cds{}, } if segment.Properties == nil { segment.Properties = make(properties.Map) diff --git a/src/segments/cds.go b/src/segments/cds.go new file mode 100644 index 00000000..f229364d --- /dev/null +++ b/src/segments/cds.go @@ -0,0 +1,68 @@ +package segments + +import ( + "encoding/json" + "oh-my-posh/environment" + "oh-my-posh/properties" +) + +type Cds struct { + language + HasDependency bool +} + +func (c *Cds) Template() string { + return languageTemplate +} + +func (c *Cds) Init(props properties.Properties, env environment.Environment) { + c.language = language{ + env: env, + props: props, + extensions: []string{".cdsrc.json", ".cdsrc-private.json", "*.cds"}, + commands: []*cmd{ + { + executable: "cds", + args: []string{"--version"}, + regex: `@sap/cds: (?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, + }, + }, + loadContext: c.loadContext, + inContext: c.inContext, + displayMode: props.GetString(DisplayMode, DisplayModeContext), + } +} + +func (c *Cds) Enabled() bool { + return c.language.Enabled() +} + +func (c *Cds) loadContext() { + if !c.language.env.HasFiles("package.json") { + return + } + + content := c.language.env.FileContent("package.json") + objmap := map[string]json.RawMessage{} + + if err := json.Unmarshal([]byte(content), &objmap); err != nil { + return + } + + dependencies := map[string]json.RawMessage{} + + if err := json.Unmarshal(objmap["dependencies"], &dependencies); err != nil { + return + } + + for d := range dependencies { + if d == "@sap/cds" { + c.HasDependency = true + break + } + } +} + +func (c *Cds) inContext() bool { + return c.HasDependency +} diff --git a/src/segments/cds_test.go b/src/segments/cds_test.go new file mode 100644 index 00000000..c6c93da1 --- /dev/null +++ b/src/segments/cds_test.go @@ -0,0 +1,146 @@ +package segments + +import ( + "fmt" + "oh-my-posh/environment" + "oh-my-posh/mock" + "oh-my-posh/properties" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCdsSegment(t *testing.T) { + cases := []struct { + Case string + ExpectedString string + ExpectedEnabled bool + File string + Template string + Version string + PackageJSON string + DisplayMode string + }{ + { + Case: "1) cds 5.5.0 - file .cdsrc.json present", + ExpectedString: "5.5.0", + ExpectedEnabled: true, + File: ".cdsrc.json", + Version: "@sap/cds: 5.5.0\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + DisplayMode: DisplayModeFiles, + }, + { + Case: "2) cds 5.5.1 - file some.cds", + ExpectedString: "5.5.1", + ExpectedEnabled: true, + File: "some.cds", + Version: "@sap/cds: 5.5.1\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + DisplayMode: DisplayModeFiles, + }, + { + Case: "3) cds 5.5.2 - no files", + ExpectedString: "", + ExpectedEnabled: false, + DisplayMode: DisplayModeFiles, + }, + { + Case: "4) cds 5.5.3 - package.json dependency", + ExpectedString: "5.5.3", + ExpectedEnabled: true, + Version: "@sap/cds: 5.5.3\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + PackageJSON: "{ \"name\": \"my-app\",\"dependencies\": { \"@sap/cds\": \"^5\" } }", + DisplayMode: DisplayModeContext, + }, + { + Case: "4) cds 5.5.4 - package.json dependency, major + minor", + ExpectedString: "5.5", + ExpectedEnabled: true, + Template: "{{ .Major }}.{{ .Minor }}", + Version: "@sap/cds: 5.5.4\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + PackageJSON: "{ \"name\": \"my-app\",\"dependencies\": { \"@sap/cds\": \"^5\" } }", + DisplayMode: DisplayModeContext, + }, + { + Case: "5) cds 5.5.5 - package.json present, no dependency, no files", + ExpectedString: "", + ExpectedEnabled: false, + Version: "@sap/cds: 5.5.5\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + PackageJSON: "{ \"name\": \"my-app\",\"dependencies\": { \"@sap/some\": \"^5\" } }", + DisplayMode: DisplayModeContext, + }, + { + Case: "6) cds 5.5.9 - display always", + ExpectedString: "5.5.9", + ExpectedEnabled: true, + Version: "@sap/cds: 5.5.9\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + PackageJSON: "{ \"name\": \"my-app\",\"dependencies\": { \"@sap/cds\": \"^5\" } }", + DisplayMode: DisplayModeAlways, + }, + { + Case: "7) cds 5.5.9 - package.json, no dependencies section", + ExpectedString: "", + ExpectedEnabled: false, + Version: "@sap/cds: 5.5.9\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + PackageJSON: "{ \"name\": \"my-app\" }", + DisplayMode: DisplayModeContext, + }, + { + Case: "8) cds 5.5.0 - file .cdsrc-private.json present", + ExpectedString: "5.5.0", + ExpectedEnabled: true, + File: ".cdsrc-private.json", + Version: "@sap/cds: 5.5.0\n@sap/cds-compiler: 2.7.0\n@sap/cds-dk: 4.5.3", + DisplayMode: DisplayModeFiles, + }, + } + + for _, tc := range cases { + var env = new(mock.MockedEnvironment) + env.On("HasCommand", "cds").Return(true) + env.On("RunCommand", "cds", []string{"--version"}).Return(tc.Version, nil) + env.On("Pwd").Return("/usr/home/dev/my-app") + env.On("Home").Return("/usr/home") + + if tc.PackageJSON != "" { + env.On("HasFiles", "package.json").Return(true) + env.On("FileContent", "package.json").Return(tc.PackageJSON) + } else { + env.On("HasFiles", "package.json").Return(false) + } + + cds := &Cds{} + + props := properties.Map{ + "display_mode": tc.DisplayMode, + } + + env.On("TemplateCache").Return(&environment.TemplateCache{ + Env: make(map[string]string), + }) + + if tc.Template == "" { + tc.Template = cds.Template() + } + + if tc.DisplayMode == "" { + tc.DisplayMode = DisplayModeContext + } + + cds.Init(props, env) + + for _, f := range cds.language.extensions { + match, err := filepath.Match(f, tc.File) + + if err != nil { + t.Fail() + } + + env.On("HasFiles", f).Return(match) + } + + failMsg := fmt.Sprintf("Failed in case: %s", tc.Case) + assert.Equal(t, tc.ExpectedEnabled, cds.Enabled(), failMsg) + assert.Equal(t, tc.ExpectedString, renderTemplate(env, tc.Template, cds), failMsg) + } +} diff --git a/themes/schema.json b/themes/schema.json index 457a93ce..634c9ab9 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -2043,6 +2043,32 @@ } } } + }, + { + "if": { + "properties": { + "type": { "const": "cds" } + } + }, + "then": { + "title": "CDS (SAP CAP) segment", + "description": "https://ohmyposh.dev/docs/cds", + "properties": { + "properties": { + "properties": { + "fetch_version": { + "$ref": "#/definitions/fetch_version" + }, + "display_mode": { + "$ref": "#/definitions/display_mode" + }, + "missing_command_text": { + "$ref": "#/definitions/missing_command_text" + } + } + } + } + } } ] }