Repository: JanDeDobbeleer/oh-my-posh
Pull request #4185 (2023-08-25 10:03:35)

Back to school
- feat(git): add SHA1 to commit metadata
- docs(templates): use correct upper-case .Var
- fix(template): support all numbers for trunc
- feat(debug): print templates

### 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

<!--
copilot:summary
-->
### <samp>🤖 Generated by Copilot at 2c293b3</samp>

This pull request adds more debug logging to various segment unit tests using the mock2 package, and introduces new segments for quasar, R, and ui5tooling. It also adds a new feature to set the console background color based on a template, and fixes a bug in the python segment. It modifies the git segment to show the full commit hash, and refactors the status segment to use an interface. It updates the wakatime segment to use the new API endpoint, and adds a new function to the template package that can glob files.

### How

<!--
copilot:walkthrough
-->
### <samp>🤖 Generated by Copilot at 2c293b3</samp>

*  Add the Sha field to the Commit struct and display it in the git segment of the prompt ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-5ca08bba45ba772ce1cbd182429db15292b228ac7c5dc0cf370dcbe55611fd92R24), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-5ca08bba45ba772ce1cbd182429db15292b228ac7c5dc0cf370dcbe55611fd92L198-R199), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-5ca08bba45ba772ce1cbd182429db15292b228ac7c5dc0cf370dcbe55611fd92R223-R224), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-7290b1c5198130d19c091496b4368edbcfd2b10d49813e6f92f01fbe26a56122R945), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-7290b1c5198130d19c091496b4368edbcfd2b10d49813e6f92f01fbe26a56122R958), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-7290b1c5198130d19c091496b4368edbcfd2b10d49813e6f92f01fbe26a56122L1000-R1002))
*  Add a new segment for quasar, which is a framework for developing web applications ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-c7626f58f00bff6e47bcd9d62b6a5a791a0d10bb96d573ed0b04c54b8ce7a295R12), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-c7626f58f00bff6e47bcd9d62b6a5a791a0d10bb96d573ed0b04c54b8ce7a295R66))
*  Add a new segment for R, which is a language for statistical computing and graphics ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-4fbe83b892e876c29809476c4821af965aae20d1b8a7988b5c4239aac05560b5R12), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-4fbe83b892e876c29809476c4821af965aae20d1b8a7988b5c4239aac05560b5R49))
*  Add a new segment for ui5tooling, which is a tool for developing SAP UI5 applications ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-f5b14641c0444558f7772a3ab1c166f81c2046c07da287dc821d8873c547f2daR13), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-f5b14641c0444558f7772a3ab1c166f81c2046c07da287dc821d8873c547f2daR135))
*  Update the wakatime segment to use the new API endpoint for getting the tracked time ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-651f007b8ed922eb28d44a9f6fad199d6b4cb8326f934df921fd332b50c62590R84), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-651f007b8ed922eb28d44a9f6fad199d6b4cb8326f934df921fd332b50c62590R131))
*  Add a new feature to set the console background color based on a template ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-66af9385bbab293dc3781f4dcc89f0567fca2335a3f6df65550ec693ed29c1bcR11), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-66af9385bbab293dc3781f4dcc89f0567fca2335a3f6df65550ec693ed29c1bcR26))
*  Add a new function to the template package that can glob files based on a pattern ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-e2798be8341a9b61461aed4b867c74423dc14bcdc50444c020854573b7e90537R10), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-e2798be8341a9b61461aed4b867c74423dc14bcdc50444c020854573b7e90537R26))
*  Refactor the status segment to use a status writer interface instead of a global variable ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-7380f036d17779bd70e9fa11bdfcccc5f83aac40d7dfe425d29e86d95d61a167R33), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-7380f036d17779bd70e9fa11bdfcccc5f83aac40d7dfe425d29e86d95d61a167R99))
*  Fix a bug in the python segment that caused the template to be ignored ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-826b535426b13dd121c764500fc941b272029316453bdd2f34344297b0bbb071R110))
*  Improve the test coverage of the path segment ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-aa603356267e7fdaa699c49f047552a020509f6da4df2d15535f9f32ddfa3959R32), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-aa603356267e7fdaa699c49f047552a020509f6da4df2d15535f9f32ddfa3959R925), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-aa603356267e7fdaa699c49f047552a020509f6da4df2d15535f9f32ddfa3959R962), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-aa603356267e7fdaa699c49f047552a020509f6da4df2d15535f9f32ddfa3959R989), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-aa603356267e7fdaa699c49f047552a020509f6da4df2d15535f9f32ddfa3959R1378), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-aa603356267e7fdaa699c49f047552a020509f6da4df2d15535f9f32ddfa3959R1412), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-aa603356267e7fdaa699c49f047552a020509f6da4df2d15535f9f32ddfa3959R1484))
*  Add more debug logging to the test cases using the mock2 package from testify and the DebugF method of the env object ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-c95d3ec80b3fecf7d41fa28718433e5976d6b4a277011f4c9e4dbeb4f28662aaR14), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-c95d3ec80b3fecf7d41fa28718433e5976d6b4a277011f4c9e4dbeb4f28662aaR129), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-beaf37732bb1e5f3820bb2574265e0152f53347cb77a6ae3c1fa9d5eda58a2b3R13), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-beaf37732bb1e5f3820bb2574265e0152f53347cb77a6ae3c1fa9d5eda58a2b3R70), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-beaf37732bb1e5f3820bb2574265e0152f53347cb77a6ae3c1fa9d5eda58a2b3R168), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-beaf37732bb1e5f3820bb2574265e0152f53347cb77a6ae3c1fa9d5eda58a2b3R229), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-bfe223a63fe9c697ab1b4b3e350056bc92afa079cec5ff58a50e7ca221ae4178R13), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-bfe223a63fe9c697ab1b4b3e350056bc92afa079cec5ff58a50e7ca221ae4178R146), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-9784825934d6b080ebfce0082a7162727e403e33d49ec83ac51e85948ce41eaaR13), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-9784825934d6b080ebfce0082a7162727e403e33d49ec83ac51e85948ce41eaaR67), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-a60b07ac95f483dbd713412256e74b4500730428da11b9070d44bbd892d4206cR12), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-a60b07ac95f483dbd713412256e74b4500730428da11b9070d44bbd892d4206cR44), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-3f7375c3c49a3f4ae4a2da5fb36ac7418982f51f186996843b4a33d00dbd1cfeR14), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-3f7375c3c49a3f4ae4a2da5fb36ac7418982f51f186996843b4a33d00dbd1cfeR34), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-462433de587506f14f53e437d1d9fac3c6c7ee8de9fb438fa858e86d166d73b5R13), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-462433de587506f14f53e437d1d9fac3c6c7ee8de9fb438fa858e86d166d73b5R82), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-0f540c408930c021f551e77d72c1d42d5ed058c59a86cec3775fe92dc8a808a9R11), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-0f540c408930c021f551e77d72c1d42d5ed058c59a86cec3775fe92dc8a808a9R58), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-9527ee169de2e73ca8fc234f541335df79a10595f77ae4667bbae5444de0c490R13), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4185/files?diff=unified&w=0#diff-9527ee169de2e73ca8fc234f541335df79a10595f77ae4667bbae5444de0c490R70))

<!---

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



Patch:
diff --git a/src/segments/git.go b/src/segments/git.go
index df120ab9..f99b9d36 100644
--- a/src/segments/git.go
+++ b/src/segments/git.go
@@ -21,6 +21,7 @@ type Commit struct {
 	Committer *User
 	Subject   string
 	Timestamp time.Time
+	Sha       string
 }
 
 type User struct {
@@ -195,7 +196,7 @@ func (g *Git) Commit() *Commit {
 		Author:    &User{},
 		Committer: &User{},
 	}
-	commitBody := g.getGitCommandOutput("log", "-1", "--pretty=format:an:%an%nae:%ae%ncn:%cn%nce:%ce%nat:%at%nsu:%s")
+	commitBody := g.getGitCommandOutput("log", "-1", "--pretty=format:an:%an%nae:%ae%ncn:%cn%nce:%ce%nat:%at%nsu:%s%nha:%H")
 	splitted := strings.Split(strings.TrimSpace(commitBody), "\n")
 	for _, line := range splitted {
 		line = strings.TrimSpace(line)
@@ -219,6 +220,8 @@ func (g *Git) Commit() *Commit {
 			}
 		case "su:":
 			g.commit.Subject = line
+		case "ha:":
+			g.commit.Sha = line
 		}
 	}
 	return g.commit
diff --git a/src/template/func_map.go b/src/template/func_map.go
index c2437e1a..82149afb 100644
--- a/src/template/func_map.go
+++ b/src/template/func_map.go
@@ -18,6 +18,7 @@ func funcMap() template.FuncMap {
 		"lt":           lt,
 		"reason":       GetReasonFromStatus,
 		"hresult":      hresult,
+		"trunc":        trunc,
 	}
 	for key, fun := range sprig.TxtFuncMap() {
 		if _, ok := funcMap[key]; !ok {
diff --git a/src/template/round.go b/src/template/round.go
index 79a828f2..7dce2772 100644
--- a/src/template/round.go
+++ b/src/template/round.go
@@ -6,23 +6,25 @@ import (
 	"strings"
 )
 
-func parseSeconds(seconds interface{}) (int, error) {
-	switch seconds := seconds.(type) {
+func toInt(integer any) (int, error) {
+	switch seconds := integer.(type) {
 	default:
-		return 0, errors.New("invalid seconds type")
+		return 0, errors.New("invalid integer type")
 	case string:
 		return strconv.Atoi(seconds)
 	case int:
 		return seconds, nil
 	case int64:
 		return int(seconds), nil
+	case uint64:
+		return int(seconds), nil
 	case float64:
 		return int(seconds), nil
 	}
 }
 
-func secondsRound(seconds interface{}) string {
-	s, err := parseSeconds(seconds)
+func secondsRound(seconds any) string {
+	s, err := toInt(seconds)
 	if err != nil {
 		return err.Error()
 	}
diff --git a/src/template/strings.go b/src/template/strings.go
new file mode 100644
index 00000000..2526986a
--- /dev/null
+++ b/src/template/strings.go
@@ -0,0 +1,18 @@
+package template
+
+func trunc(length any, s string) string {
+	c, err := toInt(length)
+	if err != nil {
+		panic(err)
+	}
+
+	if c < 0 && len(s)+c > 0 {
+		return s[len(s)+c:]
+	}
+
+	if c >= 0 && len(s) > c {
+		return s[:c]
+	}
+
+	return s
+}
diff --git a/src/template/text.go b/src/template/text.go
index 9bee237d..937141d6 100644
--- a/src/template/text.go
+++ b/src/template/text.go
@@ -67,6 +67,7 @@ func (c *Context) init(t *Text) {
 }
 
 func (t *Text) Render() (string, error) {
+	t.Env.DebugF("Rendering template: %s", t.Template)
 	if !strings.Contains(t.Template, "{{") || !strings.Contains(t.Template, "}}") {
 		return t.Template, nil
 	}
diff --git a/website/docs/configuration/templates.mdx b/website/docs/configuration/templates.mdx
index 3654d3dc..ce31e1e5 100644
--- a/website/docs/configuration/templates.mdx
+++ b/website/docs/configuration/templates.mdx
@@ -98,7 +98,7 @@ end
 
 | Name           | Type  | Description                                              |
 | -------------- | ----- | -------------------------------------------------------- |
-| `.var.VarName` | `any` | Any config variable where `VarName` is the variable name |
+| `.Var.VarName` | `any` | Any config variable where `VarName` is the variable name |
 
 ### Example
 
diff --git a/website/docs/segments/git.mdx b/website/docs/segments/git.mdx
index c6bd539c..b084d0ed 100644
--- a/website/docs/segments/git.mdx
+++ b/website/docs/segments/git.mdx
@@ -183,6 +183,7 @@ Local changes use the following syntax:
 | `.Committer` | `User`      | the comitter of the commit (see below) |
 | `.Subject`   | `string`    | the commit subject                     |
 | `.Timestamp` | `time.Time` | the commit timestamp                   |
+| `.Sha`       | `string`    | the commit SHA1                        |
 
 ### User
 


Test patch:
diff --git a/src/engine/config_test.go b/src/engine/config_test.go
index 1e4f1da5..828c8d9d 100644
--- a/src/engine/config_test.go
+++ b/src/engine/config_test.go
@@ -11,6 +11,7 @@ import (
 	"github.com/gookit/config/v2"
 	"github.com/mitchellh/mapstructure"
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func testClearDefaultConfig() {
@@ -125,6 +126,7 @@ func TestGetPalette(t *testing.T) {
 			Env:   map[string]string{},
 			Shell: "bash",
 		})
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		cfg := &Config{
 			env:      env,
 			Palette:  tc.Palette,
diff --git a/src/engine/engine_test.go b/src/engine/engine_test.go
index ca98506f..87c0447f 100644
--- a/src/engine/engine_test.go
+++ b/src/engine/engine_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/shell"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestCanWriteRPrompt(t *testing.T) {
@@ -66,6 +67,7 @@ func TestPrintPWD(t *testing.T) {
 		env.On("Shell").Return("shell")
 		env.On("User").Return("user")
 		env.On("Host").Return("host", nil)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env:   make(map[string]string),
 			Shell: "shell",
@@ -163,6 +165,7 @@ func TestGetTitle(t *testing.T) {
 		env.On("Pwd").Return(tc.Cwd)
 		env.On("Home").Return("/usr/home")
 		env.On("PathSeparator").Return(tc.PathSeparator)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: map[string]string{
 				"USERDOMAIN": "MyCompany",
@@ -223,6 +226,7 @@ func TestGetConsoleTitleIfGethostnameReturnsError(t *testing.T) {
 		env := new(mock.MockedEnvironment)
 		env.On("Pwd").Return(tc.Cwd)
 		env.On("Home").Return("/usr/home")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: map[string]string{
 				"USERDOMAIN": "MyCompany",
diff --git a/src/engine/segment_test.go b/src/engine/segment_test.go
index 52176004..fcb1da98 100644
--- a/src/engine/segment_test.go
+++ b/src/engine/segment_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/segments"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 const (
@@ -142,6 +143,7 @@ func TestGetColors(t *testing.T) {
 	}
 	for _, tc := range cases {
 		env := new(mock.MockedEnvironment)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: make(map[string]string),
 		})
diff --git a/src/segments/cf_test.go b/src/segments/cf_test.go
index 7f3a7117..768d17a9 100644
--- a/src/segments/cf_test.go
+++ b/src/segments/cf_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestCFSegment(t *testing.T) {
@@ -63,6 +64,7 @@ func TestCFSegment(t *testing.T) {
 		env.On("RunCommand", "cf", []string{"version"}).Return(tc.Version, nil)
 		env.On("Pwd").Return("/usr/home/dev/my-app")
 		env.On("Home").Return("/usr/home")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: make(map[string]string),
diff --git a/src/segments/dotnet_test.go b/src/segments/dotnet_test.go
index 2be4fd7a..4a9da832 100644
--- a/src/segments/dotnet_test.go
+++ b/src/segments/dotnet_test.go
@@ -9,6 +9,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestDotnetSegment(t *testing.T) {
@@ -40,6 +41,7 @@ func TestDotnetSegment(t *testing.T) {
 		env.On("PathSeparator").Return("")
 		env.On("Pwd").Return("/usr/home/project")
 		env.On("Home").Return("/usr/home")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: make(map[string]string),
 		})
diff --git a/src/segments/git_test.go b/src/segments/git_test.go
index 8f112bab..ecbcf593 100644
--- a/src/segments/git_test.go
+++ b/src/segments/git_test.go
@@ -942,6 +942,7 @@ func TestGitCommit(t *testing.T) {
 			ce:jan@ohmyposh.dev
 			at:1673176335
 			su:docs(error): you can't use cross segment properties
+			ha:1234567891011121314
 			`,
 			Expected: &Commit{
 				Author: &User{
@@ -954,6 +955,7 @@ func TestGitCommit(t *testing.T) {
 				},
 				Subject:   "docs(error): you can't use cross segment properties",
 				Timestamp: time.Unix(1673176335, 0),
+				Sha:       "1234567891011121314",
 			},
 		},
 		{
@@ -997,7 +999,7 @@ func TestGitCommit(t *testing.T) {
 
 	for _, tc := range cases {
 		env := new(mock.MockedEnvironment)
-		env.MockGitCommand("", tc.Output, "log", "-1", "--pretty=format:an:%an%nae:%ae%ncn:%cn%nce:%ce%nat:%at%nsu:%s")
+		env.MockGitCommand("", tc.Output, "log", "-1", "--pretty=format:an:%an%nae:%ae%ncn:%cn%nce:%ce%nat:%at%nsu:%s%nha:%H")
 		g := &Git{
 			scm: scm{
 				env:     env,
diff --git a/src/segments/golang_test.go b/src/segments/golang_test.go
index 7c94b0af..4cde06bb 100644
--- a/src/segments/golang_test.go
+++ b/src/segments/golang_test.go
@@ -11,6 +11,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 type mockedLanguageParams struct {
@@ -30,6 +31,7 @@ func getMockedLanguageEnv(params *mockedLanguageParams) (*mock.MockedEnvironment
 	env.On("TemplateCache").Return(&platform.TemplateCache{
 		Env: make(map[string]string),
 	})
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	props := properties.Map{
 		properties.FetchVersion: true,
 	}
diff --git a/src/segments/haskell_test.go b/src/segments/haskell_test.go
index 2c336f54..cb0da8e8 100644
--- a/src/segments/haskell_test.go
+++ b/src/segments/haskell_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestHaskell(t *testing.T) {
@@ -78,6 +79,7 @@ func TestHaskell(t *testing.T) {
 		env.On("HasFiles", "*.hs").Return(true)
 		env.On("Pwd").Return("/usr/home/project")
 		env.On("Home").Return("/usr/home")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: make(map[string]string),
 		})
diff --git a/src/segments/language_test.go b/src/segments/language_test.go
index 01ffd5ef..4161b0ae 100644
--- a/src/segments/language_test.go
+++ b/src/segments/language_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 const (
@@ -54,6 +55,7 @@ func bootStrapLanguageTest(args *languageArgs) *language {
 	}
 	env.On("Pwd").Return(cwd)
 	env.On("Home").Return(home)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	env.On("TemplateCache").Return(&platform.TemplateCache{
 		Env: make(map[string]string),
 	})
diff --git a/src/segments/lua_test.go b/src/segments/lua_test.go
index e390ed66..e80bc0a2 100644
--- a/src/segments/lua_test.go
+++ b/src/segments/lua_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestLua(t *testing.T) {
@@ -66,6 +67,7 @@ func TestLua(t *testing.T) {
 		env.On("HasFiles", "*.lua").Return(true)
 		env.On("Pwd").Return("/usr/home/project")
 		env.On("Home").Return("/usr/home")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: make(map[string]string),
 		})
diff --git a/src/segments/path_test.go b/src/segments/path_test.go
index 7e533656..73ef9309 100644
--- a/src/segments/path_test.go
+++ b/src/segments/path_test.go
@@ -29,6 +29,7 @@ func renderTemplateNoTrimSpace(env *mock.MockedEnvironment, segmentTemplate stri
 	}
 	env.On("Error", mock2.Anything)
 	env.On("Debug", mock2.Anything)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	tmpl := &template.Text{
 		Template: segmentTemplate,
 		Context:  context,
@@ -921,6 +922,7 @@ func TestFullPathCustomMappedLocations(t *testing.T) {
 		}
 		env.On("Flags").Return(args)
 		env.On("Shell").Return(shell.GENERIC)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: map[string]string{
 				"HOME": "/a/b/c",
@@ -957,6 +959,7 @@ func TestPowerlevelMappedLocations(t *testing.T) {
 		env.On("GOOS").Return(platform.DARWIN)
 		env.On("PathSeparator").Return("/")
 		env.On("Shell").Return(shell.GENERIC)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		path := &Path{
 			env: env,
 			props: properties.Map{
@@ -983,6 +986,7 @@ func TestFolderPathCustomMappedLocations(t *testing.T) {
 	}
 	env.On("Flags").Return(args)
 	env.On("Shell").Return(shell.GENERIC)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	path := &Path{
 		env: env,
 		props: properties.Map{
@@ -1371,6 +1375,7 @@ func TestGetPwd(t *testing.T) {
 		}
 		env.On("Flags").Return(args)
 		env.On("Shell").Return(shell.PWSH)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		path := &Path{
 			env: env,
 			props: properties.Map{
@@ -1404,6 +1409,7 @@ func TestGetFolderSeparator(t *testing.T) {
 		env.On("PathSeparator").Return("/")
 		env.On("Error", mock2.Anything)
 		env.On("Debug", mock2.Anything)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		path := &Path{
 			env: env,
 		}
@@ -1475,6 +1481,7 @@ func TestReplaceMappedLocations(t *testing.T) {
 		env.On("Shell").Return(shell.FISH)
 		env.On("GOOS").Return(platform.DARWIN)
 		env.On("Home").Return("/a/b/k")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		path := &Path{
 			env: env,
 			props: properties.Map{
diff --git a/src/segments/python_test.go b/src/segments/python_test.go
index 7dd72e32..a58fee12 100644
--- a/src/segments/python_test.go
+++ b/src/segments/python_test.go
@@ -107,6 +107,7 @@ func TestPythonTemplate(t *testing.T) {
 		env.On("Pwd").Return("/usr/home/project")
 		env.On("Home").Return("/usr/home")
 		env.On("ResolveSymlink", mock2.Anything).Return(tc.ResolveSymlink.Path, tc.ResolveSymlink.Err)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		props := properties.Map{
 			properties.FetchVersion: tc.FetchVersion,
 			UsePythonVersionFile:    true,
diff --git a/src/segments/quasar_test.go b/src/segments/quasar_test.go
index 4515345b..c7c5dfff 100644
--- a/src/segments/quasar_test.go
+++ b/src/segments/quasar_test.go
@@ -9,6 +9,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/mock"
 	"github.com/jandedobbeleer/oh-my-posh/src/platform"
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestQuasar(t *testing.T) {
@@ -62,6 +63,7 @@ func TestQuasar(t *testing.T) {
 		env.On("RunCommand", "quasar", []string{"--version"}).Return(tc.Version, nil)
 		env.On("Pwd").Return("/usr/home/project")
 		env.On("Home").Return("/usr/home")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: make(map[string]string),
 		})
diff --git a/src/segments/r_test.go b/src/segments/r_test.go
index ba71c33b..32186761 100644
--- a/src/segments/r_test.go
+++ b/src/segments/r_test.go
@@ -9,6 +9,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestR(t *testing.T) {
@@ -45,6 +46,7 @@ func TestR(t *testing.T) {
 		env.On("HasFiles", "*.R").Return(true)
 		env.On("Pwd").Return("/usr/home/project")
 		env.On("Home").Return("/usr/home")
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: make(map[string]string),
 		})
diff --git a/src/segments/status_test.go b/src/segments/status_test.go
index 11b2d870..170df51e 100644
--- a/src/segments/status_test.go
+++ b/src/segments/status_test.go
@@ -30,6 +30,7 @@ func TestStatusWriterEnabled(t *testing.T) {
 			Code: 133,
 		})
 		env.On("Error", mock2.Anything).Return(nil)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 
 		props := properties.Map{}
 		if len(tc.Template) > 0 {
@@ -95,6 +96,7 @@ func TestFormatStatus(t *testing.T) {
 			Code: 133,
 		})
 		env.On("Error", mock2.Anything).Return(nil)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		props := properties.Map{
 			StatusTemplate:  tc.Template,
 			StatusSeparator: tc.Separator,
diff --git a/src/segments/ui5tooling_test.go b/src/segments/ui5tooling_test.go
index b5da9121..c47ceb92 100644
--- a/src/segments/ui5tooling_test.go
+++ b/src/segments/ui5tooling_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/properties"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 const (
@@ -131,6 +132,7 @@ func prepareMockedEnvironment(tc *testCase) *mock.MockedEnvironment {
 	env.On("RunCommand", "ui5", []string{"--version"}).Return(tc.Version, nil)
 	env.On("Home").Return("/home/user")
 	env.On("Pwd").Return(WorkingDirRoot)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 
 	env.On("TemplateCache").Return(&platform.TemplateCache{
 		Env: make(map[string]string),
diff --git a/src/segments/wakatime_test.go b/src/segments/wakatime_test.go
index 2070f9da..31e29e3c 100644
--- a/src/segments/wakatime_test.go
+++ b/src/segments/wakatime_test.go
@@ -81,6 +81,7 @@ func TestWTTrackedTime(t *testing.T) {
 		cache.On("Get", FAKEAPIURL).Return(response, !tc.CacheFoundFail)
 		cache.On("Set", FAKEAPIURL, response, tc.CacheTimeout).Return()
 		env.On("Cache").Return(cache)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: map[string]string{"HELLO": "hello"},
@@ -127,6 +128,7 @@ func TestWTGetUrl(t *testing.T) {
 		env := &mock.MockedEnvironment{}
 
 		env.On("Error", mock2.Anything)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: map[string]string{"HELLO": "hello"},
 		})
diff --git a/src/shell/init_test.go b/src/shell/init_test.go
index 19cb922e..4ab1e008 100644
--- a/src/shell/init_test.go
+++ b/src/shell/init_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/platform"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestConsoleBackgroundColorTemplate(t *testing.T) {
@@ -22,6 +23,7 @@ func TestConsoleBackgroundColorTemplate(t *testing.T) {
 
 	for _, tc := range cases {
 		env := new(mock.MockedEnvironment)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		env.On("TemplateCache").Return(&platform.TemplateCache{
 			Env: map[string]string{
 				"TERM_PROGRAM": tc.Term,
diff --git a/src/template/files_test.go b/src/template/files_test.go
index 8eec68b1..ac7f1f55 100644
--- a/src/template/files_test.go
+++ b/src/template/files_test.go
@@ -7,6 +7,7 @@ import (
 	"github.com/jandedobbeleer/oh-my-posh/src/platform"
 
 	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
 )
 
 func TestGlob(t *testing.T) {
@@ -22,6 +23,7 @@ func TestGlob(t *testing.T) {
 	}
 
 	env := &mock.MockedEnvironment{}
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	env.On("TemplateCache").Return(&platform.TemplateCache{
 		Env: make(map[string]string),
 	})
diff --git a/src/template/link_test.go b/src/template/link_test.go
index fe515763..f0349092 100644
--- a/src/template/link_test.go
+++ b/src/template/link_test.go
@@ -27,6 +27,7 @@ func TestUrl(t *testing.T) {
 	})
 	env.On("Error", mock2.Anything)
 	env.On("Debug", mock2.Anything)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	for _, tc := range cases {
 		tmpl := &Text{
 			Template: tc.Template,
@@ -52,6 +53,7 @@ func TestPath(t *testing.T) {
 	}
 
 	env := &mock.MockedEnvironment{}
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	env.On("TemplateCache").Return(&platform.TemplateCache{
 		Env: make(map[string]string),
 	})
diff --git a/src/template/numbers_test.go b/src/template/numbers_test.go
index 7d2f809d..9d8d9c4e 100644
--- a/src/template/numbers_test.go
+++ b/src/template/numbers_test.go
@@ -27,6 +27,7 @@ func TestHResult(t *testing.T) {
 	})
 	env.On("Error", mock2.Anything)
 	env.On("Debug", mock2.Anything)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	for _, tc := range cases {
 		tmpl := &Text{
 			Template: tc.Template,
diff --git a/src/template/round_test.go b/src/template/round_test.go
index d31fddfe..d1af9781 100644
--- a/src/template/round_test.go
+++ b/src/template/round_test.go
@@ -34,6 +34,7 @@ func TestRoundSeconds(t *testing.T) {
 	})
 	env.On("Error", mock2.Anything)
 	env.On("Debug", mock2.Anything)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	for _, tc := range cases {
 		tmpl := &Text{
 			Template: tc.Template,
diff --git a/src/template/strings_test.go b/src/template/strings_test.go
new file mode 100644
index 00000000..154853f9
--- /dev/null
+++ b/src/template/strings_test.go
@@ -0,0 +1,48 @@
+package template
+
+import (
+	"testing"
+
+	"github.com/jandedobbeleer/oh-my-posh/src/mock"
+	"github.com/jandedobbeleer/oh-my-posh/src/platform"
+
+	"github.com/stretchr/testify/assert"
+	mock2 "github.com/stretchr/testify/mock"
+)
+
+func TestTrunc(t *testing.T) {
+	cases := []struct {
+		Case        string
+		Expected    string
+		Template    string
+		ShouldError bool
+	}{
+		{Case: "5 length integer", Expected: "Hello", Template: `{{ trunc 5 "Hello World" }}`},
+		{Case: "5 length stringteger", Expected: "Hello", Template: `{{ trunc "5" "Hello World" }}`},
+		{Case: "5 length float", Expected: "Hello", Template: `{{ trunc 5.0 "Hello World" }}`},
+		{Case: "invalid", ShouldError: true, Template: `{{ trunc "foo" "Hello World" }}`},
+		{Case: "smaller than length", Expected: "Hello World", Template: `{{ trunc 20 "Hello World" }}`},
+		{Case: "negative", Expected: "ld", Template: `{{ trunc -2 "Hello World" }}`},
+	}
+
+	env := &mock.MockedEnvironment{}
+	env.On("TemplateCache").Return(&platform.TemplateCache{
+		Env: make(map[string]string),
+	})
+	env.On("Error", mock2.Anything)
+	env.On("Debug", mock2.Anything)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
+	for _, tc := range cases {
+		tmpl := &Text{
+			Template: tc.Template,
+			Context:  nil,
+			Env:      env,
+		}
+		text, err := tmpl.Render()
+		if tc.ShouldError {
+			assert.Error(t, err)
+			continue
+		}
+		assert.Equal(t, tc.Expected, text, tc.Case)
+	}
+}
diff --git a/src/template/text_test.go b/src/template/text_test.go
index b0d4d428..2e33953d 100644
--- a/src/template/text_test.go
+++ b/src/template/text_test.go
@@ -160,6 +160,7 @@ func TestRenderTemplate(t *testing.T) {
 		Env: make(map[string]string),
 	})
 	env.On("Error", mock2.Anything)
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	for _, tc := range cases {
 		tmpl := &Text{
 			Template: tc.Template,
@@ -245,6 +246,7 @@ func TestRenderTemplateEnvVar(t *testing.T) {
 			OS:  "darwin",
 		})
 		env.On("Error", mock2.Anything)
+		env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 		tmpl := &Text{
 			Template: tc.Template,
 			Context:  tc.Context,
@@ -354,6 +356,7 @@ func TestSegmentContains(t *testing.T) {
 	env := &mock.MockedEnvironment{}
 	segments := platform.NewConcurrentMap()
 	segments.Set("Git", "foo")
+	env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
 	env.On("TemplateCache").Return(&platform.TemplateCache{
 		Env:      make(map[string]string),
 		Segments: segments,