Repository: JanDeDobbeleer/oh-my-posh
Pull request #657 (2021-04-17 11:47:37)

feat(node): show NPM/Yarn context
### Prerequisites

- [x] I have read and understand 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

[Description of the change]

[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary



Patch:
diff --git a/docs/docs/segment-node.md b/docs/docs/segment-node.md
index 3e1e9dbc..a9451121 100644
--- a/docs/docs/segment-node.md
+++ b/docs/docs/segment-node.md
@@ -36,3 +36,6 @@ returned node version
 - color_background: `boolean` - color the background or foreground for `version_mismatch_color` - defaults to `false`
 - version_mismatch_color: `string` [color][colors] - the color to use for `enable_version_mismatch` - defaults to
 segment's background or foreground color
+- display_package_manager: `boolean` - show whether the current project uses Yarn or NPM - defaults to `false`
+- yarn_icon: `string` - the icon/text to display when using Yarn - defaults to ` \uF61A`
+- npm_icon: `string` - the icon/text to display when using NPM - defaults to ` \uE71E`
diff --git a/src/segment_node.go b/src/segment_node.go
index 1a19e845..a764fe38 100644
--- a/src/segment_node.go
+++ b/src/segment_node.go
@@ -1,11 +1,24 @@
 package main
 
+import "fmt"
+
 type node struct {
-	language *language
+	language           *language
+	packageManagerIcon string
 }
 
+const (
+	// YarnIcon illustrates Yarn is used
+	YarnIcon Property = "yarn_icon"
+	// NPMIcon illustrates NPM is used
+	NPMIcon Property = "npm_icon"
+	// DisplayPackageManager shows if NPM or Yarn is used
+	DisplayPackageManager Property = "display_package_manager"
+)
+
 func (n *node) string() string {
-	return n.language.string()
+	version := n.language.string()
+	return fmt.Sprintf("%s%s", version, n.packageManagerIcon)
 }
 
 func (n *node) init(props *properties, env environmentInfo) {
@@ -22,6 +35,7 @@ func (n *node) init(props *properties, env environmentInfo) {
 		},
 		versionURLTemplate: "[%[1]s](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V%[2]s.md#%[1]s)",
 		matchesVersionFile: n.matchesVersionFile,
+		loadContext:        n.loadContext,
 	}
 }
 
@@ -29,6 +43,18 @@ func (n *node) enabled() bool {
 	return n.language.enabled()
 }
 
+func (n *node) loadContext() {
+	if !n.language.props.getBool(DisplayPackageManager, false) {
+		return
+	}
+	if n.language.env.hasFiles("yarn.lock") {
+		n.packageManagerIcon = n.language.props.getString(YarnIcon, " \uF61A")
+	}
+	if n.language.env.hasFiles("package-lock.json") || n.language.env.hasFiles("package.json") {
+		n.packageManagerIcon = n.language.props.getString(NPMIcon, " \uE71E")
+	}
+}
+
 func (n *node) matchesVersionFile() bool {
 	fileVersion := n.language.env.getFileContent(".nvmrc")
 	if len(fileVersion) == 0 {
diff --git a/src/segment_python.go b/src/segment_python.go
index 002253d0..895564e0 100644
--- a/src/segment_python.go
+++ b/src/segment_python.go
@@ -13,7 +13,7 @@ const (
 )
 
 func (p *python) string() string {
-	if p.venvName == "" || !p.language.props.getBool(DisplayVirtualEnv, true) {
+	if p.venvName == "" {
 		return p.language.string()
 	}
 	version := p.language.string()
@@ -51,6 +51,9 @@ func (p *python) enabled() bool {
 }
 
 func (p *python) loadContext() {
+	if !p.language.props.getBool(DisplayVirtualEnv, true) {
+		return
+	}
 	venvVars := []string{
 		"VIRTUAL_ENV",
 		"CONDA_ENV_PATH",
diff --git a/themes/schema.json b/themes/schema.json
index c08ed228..de4357e4 100644
--- a/themes/schema.json
+++ b/themes/schema.json
@@ -845,6 +845,24 @@
                   },
                   "missing_command_text": {
                     "$ref": "#/definitions/missing_command_text"
+                  },
+                  "display_package_manager": {
+                    "type": "boolean",
+                    "title": "Display Package Manager",
+                    "description": "Show whether the current project uses Yarn or NPM",
+                    "default": false
+                  },
+                  "yarn_icon": {
+                    "type": "string",
+                    "title": "Yarn Icon",
+                    "description": "Icon/text to use for Yarn",
+                    "default": " \uF61A"
+                  },
+                  "npm_icon": {
+                    "type": "string",
+                    "title": "NPM Icon",
+                    "description": "Icon/text to use for NPM",
+                    "default": " \uE71E"
                   }
                 }
               }


Test patch:
diff --git a/src/segment_node_test.go b/src/segment_node_test.go
index 9c5e38b9..c7bc7206 100644
--- a/src/segment_node_test.go
+++ b/src/segment_node_test.go
@@ -34,3 +34,41 @@ func TestNodeMatchesVersionFile(t *testing.T) {
 		assert.Equal(t, tc.Expected, node.matchesVersionFile(), tc.Case)
 	}
 }
+
+func TestNodeInContext(t *testing.T) {
+	cases := []struct {
+		Case           string
+		HasYarn        bool
+		hasNPM         bool
+		hasDefault     bool
+		PkgMgrEnabled  bool
+		ExpectedString string
+	}{
+		{Case: "no package manager file", ExpectedString: "", PkgMgrEnabled: true},
+		{Case: "yarn", HasYarn: true, ExpectedString: "yarn", PkgMgrEnabled: true},
+		{Case: "npm", hasNPM: true, ExpectedString: "npm", PkgMgrEnabled: true},
+		{Case: "default", hasDefault: true, ExpectedString: "npm", PkgMgrEnabled: true},
+		{Case: "disabled", HasYarn: true, ExpectedString: "", PkgMgrEnabled: false},
+	}
+
+	for _, tc := range cases {
+		env := new(MockedEnvironment)
+		env.On("hasFiles", "yarn.lock").Return(tc.HasYarn)
+		env.On("hasFiles", "package-lock.json").Return(tc.hasNPM)
+		env.On("hasFiles", "package.json").Return(tc.hasDefault)
+		node := &node{
+			language: &language{
+				env: env,
+				props: &properties{
+					values: map[Property]interface{}{
+						YarnIcon:              "yarn",
+						NPMIcon:               "npm",
+						DisplayPackageManager: tc.PkgMgrEnabled,
+					},
+				},
+			},
+		}
+		node.loadContext()
+		assert.Equal(t, tc.ExpectedString, node.packageManagerIcon, tc.Case)
+	}
+}