Merge branch 'main' into main

This commit is contained in:
Luis 2024-05-23 14:31:01 -04:00 committed by GitHub
commit d37994e18c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
193 changed files with 4226 additions and 1532 deletions

View file

@ -2280,6 +2280,152 @@
"contributions": [
"code"
]
},
{
"login": "gergelyk",
"name": "Grzegorz Krasoń",
"avatar_url": "https://avatars.githubusercontent.com/u/11185582?v=4",
"profile": "http://krason.dev/",
"contributions": [
"code"
]
},
{
"login": "rockyoung",
"name": "rockyoung",
"avatar_url": "https://avatars.githubusercontent.com/u/1207971?v=4",
"profile": "https://github.com/rockyoung",
"contributions": [
"code",
"doc"
]
},
{
"login": "shravanasati",
"name": "Shravan Asati",
"avatar_url": "https://avatars.githubusercontent.com/u/69118069?v=4",
"profile": "https://github.com/shravanasati",
"contributions": [
"design"
]
},
{
"login": "lzecca78",
"name": "Luca Zecca",
"avatar_url": "https://avatars.githubusercontent.com/u/3881844?v=4",
"profile": "https://github.com/lzecca78",
"contributions": [
"code",
"design",
"doc"
]
},
{
"login": "jreilly-lukava",
"name": "Joshua Reilly",
"avatar_url": "https://avatars.githubusercontent.com/u/30353736?v=4",
"profile": "https://github.com/jreilly-lukava",
"contributions": [
"code",
"doc"
]
},
{
"login": "ivan-the-terrible",
"name": "Ivan",
"avatar_url": "https://avatars.githubusercontent.com/u/56458442?v=4",
"profile": "https://ivan-the-terrible.github.io/",
"contributions": [
"design",
"doc",
"code"
]
},
{
"login": "mountcount",
"name": "mountcount",
"avatar_url": "https://avatars.githubusercontent.com/u/166301065?v=4",
"profile": "https://github.com/mountcount",
"contributions": [
"doc"
]
},
{
"login": "Bondrake",
"name": "Bondrake",
"avatar_url": "https://avatars.githubusercontent.com/u/11696?v=4",
"profile": "https://github.com/Bondrake",
"contributions": [
"design",
"code"
]
},
{
"login": "R00dRallec",
"name": "R00dRallec",
"avatar_url": "https://avatars.githubusercontent.com/u/9081954?v=4",
"profile": "https://github.com/R00dRallec",
"contributions": [
"code",
"doc"
]
},
{
"login": "publicfacingusername",
"name": "Justin Wolfington",
"avatar_url": "https://avatars.githubusercontent.com/u/13956145?v=4",
"profile": "https://github.com/publicfacingusername",
"contributions": [
"code"
]
},
{
"login": "jtracey93",
"name": "Jack Tracey",
"avatar_url": "https://avatars.githubusercontent.com/u/41163455?v=4",
"profile": "https://bio.link/jacktracey",
"contributions": [
"design"
]
},
{
"login": "MarkDaveny",
"name": "MarkDaveny",
"avatar_url": "https://avatars.githubusercontent.com/u/168091250?v=4",
"profile": "https://github.com/MarkDaveny",
"contributions": [
"code",
"design",
"doc"
]
},
{
"login": "tiwahu",
"name": "Timothy Huber",
"avatar_url": "https://avatars.githubusercontent.com/u/590564?v=4",
"profile": "http://www.tiwahu.com/",
"contributions": [
"design"
]
},
{
"login": "YashJM",
"name": "Yash Mistry",
"avatar_url": "https://avatars.githubusercontent.com/u/63824041?v=4",
"profile": "http://yashjmistry.me",
"contributions": [
"design"
]
},
{
"login": "jlabonski",
"name": "Jeffrey Labonski",
"avatar_url": "https://avatars.githubusercontent.com/u/2981369?v=4",
"profile": "https://github.com/jlabonski",
"contributions": [
"code",
"doc"
]
}
],
"contributorsPerLine": 7,

View file

@ -8,7 +8,7 @@
// Update the VARIANT arg to pick a version of Go: 1, 1.16, 1.17
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local arm64/Apple Silicon.
"VARIANT": "1-1.21-bullseye",
"VARIANT": "1-1.22.3-bullseye",
// Options:
"POSH_THEME": "/workspaces/oh-my-posh/themes/jandedobbeleer.omp.json",

3
.github/holopin.yml vendored
View file

@ -4,3 +4,6 @@ stickers:
-
id: clg0u51g681700fmfr086ofc6
alias: wizard
-
id: clu72f66x59170fjoo6t2b7zs
alias: helping

View file

@ -9,7 +9,7 @@ jobs:
container: ghcr.io/jandedobbeleer/golang-android-container:latest
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Build
run: |
VERSION=$(echo "${{ github.event.release.name }}" | cut -c2-)

View file

@ -17,18 +17,18 @@ jobs:
shell: pwsh
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Install Go 🗳
uses: ./.github/workflows/composite/bootstrap-go
- name: Run GoReleaser 🚀
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8
uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811
with:
distribution: goreleaser
version: latest
args: build --clean --snapshot --skip=post-hooks --skip=before
workdir: src
- name: Archive production artifacts
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
with:
name: builds
retention-days: 1

View file

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Check and close 🔐
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
with:

View file

@ -20,14 +20,14 @@ jobs:
working-directory: ${{ github.workspace }}/src
steps:
- name: Install Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
with:
go-version: 1.21
cache-dependency-path: src/go.sum
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Golang CI
uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804
uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64
with:
working-directory: src
- name: Unit Tests

View file

@ -20,17 +20,17 @@ jobs:
working-directory: ${{ github.workspace }}/src
steps:
- name: Install Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
with:
go-version: 1.21
cache-dependency-path: src/go.sum
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Initialize CodeQL
uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a
uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276
with:
languages: go
- name: Autobuild
uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a
uses: github/codeql-action/autobuild@9fdb3e49720b44c48891d036bb502feb25684276
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a
uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276

View file

@ -9,4 +9,4 @@ runs:
steps:
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
with:
go-version: '1.22.0'
go-version: '1.22.3'

View file

@ -18,12 +18,12 @@ jobs:
runs-on: ubuntu-latest
name: Build and Deploy
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
with:
submodules: true
persist-credentials: false
- name: Install Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
with:
go-version: 1.21
cache-dependency-path: src/go.sum
@ -34,7 +34,7 @@ jobs:
# Images are defined on every Kind release
# See https://github.com/kubernetes-sigs/kind/releases
- name: Create k8s v1.23 Kind Cluster
uses: helm/kind-action@99576bfa6ddf9a8e612d83b513da5a75875caced
uses: helm/kind-action@0025e74a8c7512023d06dc019c617aa3cf561fde
with:
node_image: kindest/node:v1.23.4@sha256:0e34f0d0fd448aa2f2819cfd74e99fe5793a6e4938b328f657c8e3f81ee0dfb9
cluster_name: posh
@ -44,7 +44,7 @@ jobs:
- name: Set default Kubernetes namespace
run: |
kubectl config set-context posh --namespace demo
- uses: azure/login@cb79c773a3cfa27f31f25eb3f677781210c9ce3d
- uses: azure/login@6b2456866fc08b011acb422a92a4aa20e2c4de32
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Build oh-my-posh 🔧

View file

@ -10,12 +10,12 @@ jobs:
working-directory: ${{ github.workspace }}/src
steps:
- name: Install Go
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
with:
go-version: 1.21
cache-dependency-path: src/go.sum
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Check for unused dependencies
run: |
go mod tidy

View file

@ -15,7 +15,7 @@ jobs:
working-directory: ${{ github.workspace }}/packages/inno
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Build installer 📦
id: build
env:

View file

@ -16,7 +16,7 @@ jobs:
steps:
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771
with:
issue-inactive-days: '182'
issue-inactive-days: '90'
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity (i.e. last half year) after it was closed.

View file

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Lint files
uses: articulate/actions-markdownlint@17b8abe7407cd17590c006ecc837c35e1ac3ed83
with:

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Check and merge ⛙
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
with:

View file

@ -19,10 +19,10 @@ jobs:
skipped: ${{ steps.changelog.outputs.skipped }}
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Create changelog ✍️
id: changelog
uses: TriPSs/conventional-changelog-action@f04dac1fd07687cec8ea302937ca588b33786e98
uses: TriPSs/conventional-changelog-action@3a392e9aa44a72686b0fc13259a90d287dd0877c
with:
github-token: ${{ secrets.github_token }}
skip-version-file: "true"
@ -41,7 +41,7 @@ jobs:
working-directory: ${{ github.workspace }}/src
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Install Go 🗳
uses: ./.github/workflows/composite/bootstrap-go
- name: Tag HEAD 😸
@ -50,7 +50,7 @@ jobs:
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag ${{ needs.changelog.outputs.tag }}
- name: Install cosign 🔑
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20
with:
cosign-release: 'v1.4.0'
- name: Private Key 🔐
@ -60,7 +60,7 @@ jobs:
env:
COSIGN_KEY: ${{secrets.COSIGN_KEY}}
- name: Run GoReleaser 🚀
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8
uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811
with:
distribution: goreleaser
version: latest
@ -85,7 +85,7 @@ jobs:
}
shell: pwsh
- name: Release 🎓
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844
uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87
with:
tag_name: ${{ needs.changelog.outputs.tag }}
body: ${{ needs.changelog.outputs.body }}

View file

@ -18,7 +18,7 @@ jobs:
working-directory: ${{ github.workspace }}/packages/scoop
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Update Template ✍️
run: |
./build.ps1 -Version ${{ github.event.inputs.version }}

View file

@ -17,7 +17,7 @@ jobs:
WINGETCREATE_TOKEN: ${{ secrets.WINGETCREATE_TOKEN }}
steps:
- name: Checkout code 👋
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
- name: Create manifest and submit PR 📦
run: |
./build.ps1 -Version "${{ github.event.inputs.version }}" -Token $env:WINGETCREATE_TOKEN

11
.vscode/launch.json vendored
View file

@ -77,6 +77,17 @@
"--print"
]
},
{
"name": "Export Config",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceRoot}/src",
"args": [
"config",
"export"
]
},
{
"name": "Export Image",
"type": "go",

View file

@ -8,7 +8,6 @@ linters:
- dupl
- errcheck
- exhaustive
- gochecknoinits
- goconst
- gocritic
- gocyclo
@ -36,13 +35,11 @@ linters-settings:
gocritic:
enabled-tags:
- diagnostic
- experimental
# - experimental
- opinionated
- performance
- style
disabled-tags:
- experimental
disabled-checks:
- ifElseChain
lll:
line-length: 180

View file

@ -9,7 +9,7 @@ import (
"github.com/mattn/go-runewidth"
)
func init() { //nolint:gochecknoinits
func init() {
runewidth.DefaultCondition.EastAsianWidth = false
}
@ -36,8 +36,8 @@ type style struct {
}
type Colors struct {
Background string `json:"background"`
Foreground string `json:"foreground"`
Background string `json:"background" toml:"background"`
Foreground string `json:"foreground" toml:"foreground"`
}
const (
@ -114,6 +114,10 @@ type Writer struct {
hyperlinkStart string
hyperlinkCenter string
hyperlinkEnd string
iTermPromptMark string
iTermCurrentDir string
iTermRemoteHost string
}
func (w *Writer) Init(shellName string) {
@ -137,6 +141,9 @@ func (w *Writer) Init(shellName string) {
w.osc99 = "\\[\x1b]9;9;%s\x1b\\\\\\]"
w.osc7 = "\\[\x1b]7;file://%s/%s\x1b\\\\\\]"
w.osc51 = "\\[\x1b]51;A;%s@%s:%s\x1b\\\\\\]"
w.iTermPromptMark = "\\[$(iterm2_prompt_mark)\\]"
w.iTermCurrentDir = "\\[\x1b]1337;CurrentDir=%s\x07\\]"
w.iTermRemoteHost = "\\[\x1b]1337;RemoteHost=%s@%s\x07\\]"
case shell.ZSH, shell.TCSH:
w.format = "%%{%s%%}"
w.linechange = "%%{\x1b[%d%s%%}"
@ -154,6 +161,9 @@ func (w *Writer) Init(shellName string) {
w.osc99 = "%%{\x1b]9;9;%s\x1b\\%%}"
w.osc7 = "%%{\x1b]7;file://%s/%s\x1b\\%%}"
w.osc51 = "%%{\x1b]51;A%s@%s:%s\x1b\\%%}"
w.iTermPromptMark = "%{$(iterm2_prompt_mark)%}"
w.iTermCurrentDir = "%%{\x1b]1337;CurrentDir=%s\x07%%}"
w.iTermRemoteHost = "%%{\x1b]1337;RemoteHost=%s@%s\x07%%}"
default:
w.linechange = "\x1b[%d%s"
w.left = "\x1b[%dD"
@ -171,6 +181,8 @@ func (w *Writer) Init(shellName string) {
w.osc99 = "\x1b]9;9;%s\x1b\\"
w.osc7 = "\x1b]7;file://%s/%s\x1b\\"
w.osc51 = "\x1b]51;A%s@%s:%s\x1b\\"
w.iTermCurrentDir = "\x1b]1337;CurrentDir=%s\x07"
w.iTermRemoteHost = "\x1b]1337;RemoteHost=%s@%s\x07"
}
}
@ -428,7 +440,7 @@ func (w *Writer) writeSegmentColors() {
return
}
if fg.IsTransparent() && len(w.TerminalBackground) != 0 {
if fg.IsTransparent() && len(w.TerminalBackground) != 0 { //nolint: gocritic
background := w.getAnsiFromColorString(w.TerminalBackground, false)
w.writeEscapedAnsiString(fmt.Sprintf(colorise, background))
w.writeEscapedAnsiString(fmt.Sprintf(colorise, bg.ToForeground()))

51
src/ansi/iterm.go Normal file
View file

@ -0,0 +1,51 @@
package ansi
import (
"fmt"
"slices"
"strings"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
)
type iTermFeature string
const (
PromptMark iTermFeature = "prompt_mark"
CurrentDir iTermFeature = "current_dir"
RemoteHost iTermFeature = "remote_host"
)
type ITermFeatures []iTermFeature
func (f ITermFeatures) Contains(feature iTermFeature) bool {
for _, item := range f {
if item == feature {
return true
}
}
return false
}
func (w *Writer) RenderItermFeatures(features ITermFeatures, sh, pwd, user, host string) string {
supportedShells := []string{shell.BASH, shell.ZSH}
var result strings.Builder
for _, feature := range features {
switch feature {
case PromptMark:
if !slices.Contains(supportedShells, sh) {
continue
}
result.WriteString(w.iTermPromptMark)
case CurrentDir:
result.WriteString(fmt.Sprintf(w.iTermCurrentDir, pwd))
case RemoteHost:
result.WriteString(fmt.Sprintf(w.iTermRemoteHost, user, host))
}
}
return result.String()
}

View file

@ -1,6 +1,6 @@
package ansi
type Palettes struct {
Template string `json:"template,omitempty"`
List map[string]Palette `json:"list,omitempty"`
Template string `json:"template,omitempty" toml:"template,omitempty"`
List map[string]Palette `json:"list,omitempty" toml:"list,omitempty"`
}

View file

@ -57,7 +57,7 @@ You can do the following:
},
}
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(getCache)
}

View file

@ -39,6 +39,6 @@ You can export, migrate or edit the config.`,
},
}
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(configCmd)
}

View file

@ -2,7 +2,9 @@ package cli
import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"github.com/jandedobbeleer/oh-my-posh/src/engine"
@ -42,15 +44,41 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
env.Init()
defer env.Close()
cfg := engine.LoadConfig(env)
if len(output) == 0 && len(format) == 0 {
// usage error
os.Exit(2)
}
formats := []string{"json", "jsonc", "toml", "tml", "yaml", "yml"}
if len(format) != 0 && !slices.Contains(formats, format) {
// usage error
os.Exit(2)
}
if len(output) == 0 {
fmt.Print(cfg.Export(format))
return
}
cfg.Output = cleanOutputPath(output, env)
format := strings.TrimPrefix(filepath.Ext(output), ".")
if format == "yml" {
format = engine.YAML
if len(format) == 0 {
format = strings.TrimPrefix(filepath.Ext(output), ".")
}
switch format {
case "json", "jsonc":
format = engine.JSON
case "toml", "tml":
format = engine.TOML
case "yaml", "yml":
format = engine.YAML
default:
// data error
os.Exit(65)
}
cfg.Write(format)
},
}
@ -68,7 +96,7 @@ func cleanOutputPath(path string, env platform.Environment) string {
return filepath.Clean(path)
}
func init() { //nolint:gochecknoinits
func init() {
exportCmd.Flags().StringVarP(&format, "format", "f", "json", "config format to migrate to")
exportCmd.Flags().StringVarP(&output, "output", "o", "", "config file to export to")
configCmd.AddCommand(exportCmd)

View file

@ -106,11 +106,9 @@ Exports the config to an image file using customized output options.`,
},
}
func init() { //nolint:gochecknoinits
func init() {
imageCmd.Flags().StringVar(&author, "author", "", "config author")
imageCmd.Flags().StringVar(&bgColor, "background-color", "", "image background color")
// imageCmd.Flags().IntVar(&cursorPadding, "cursor-padding", 0, "prompt cursor padding")
// imageCmd.Flags().IntVar(&rPromptOffset, "rprompt-offset", 0, "right prompt offset")
imageCmd.Flags().StringVarP(&outputImage, "output", "o", "", "image file (.png) to export to")
exportCmd.AddCommand(imageCmd)
}

View file

@ -57,7 +57,7 @@ A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
},
}
func init() { //nolint:gochecknoinits
func init() {
migrateCmd.Flags().BoolVarP(&write, "write", "w", false, "write the migrated config back to the config file")
migrateCmd.Flags().StringVarP(&format, "format", "f", "json", "the config format to migrate to")
configCmd.AddCommand(migrateCmd)

View file

@ -59,7 +59,7 @@ A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
},
}
func init() { //nolint:gochecknoinits
func init() {
migrateGlyphsCmd.Flags().BoolVarP(&write, "write", "w", false, "write the migrated config back to the config file")
migrateGlyphsCmd.Flags().StringVarP(&format, "format", "f", "", "the config format to migrate to")
migrateCmd.AddCommand(migrateGlyphsCmd)

View file

@ -60,7 +60,7 @@ var debugCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
debugCmd.Flags().StringVar(&pwd, "pwd", "", "current working directory")
debugCmd.Flags().StringVar(&shellName, "shell", "", "the shell to print for")
debugCmd.Flags().BoolVarP(&plain, "plain", "p", false, "plain text output (no ANSI)")

View file

@ -22,6 +22,6 @@ var disableCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(disableCmd)
}

View file

@ -37,7 +37,7 @@ var enableCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(enableCmd)
}
@ -55,6 +55,7 @@ func toggleFeature(cmd *cobra.Command, feature string, enable bool) {
env.Cache().Delete(upgrade.CACHEKEY)
return
}
env.Cache().Set(upgrade.CACHEKEY, "disabled", -1)
default:
_ = cmd.Help()

View file

@ -65,7 +65,7 @@ This command is used to install fonts and configure the font in your terminal.
}
)
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(fontCmd)
fontCmd.Flags().BoolVar(&user, "user", false, "install font as user")
}

View file

@ -91,7 +91,7 @@ This command is used to get the value of the following variables:
},
}
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(getCmd)
getCmd.Flags().StringVar(&shellName, "shell", "", "the shell to print for")
}

View file

@ -3,9 +3,11 @@ package cli
import (
"fmt"
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
"github.com/jandedobbeleer/oh-my-posh/src/engine"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
"github.com/spf13/cobra"
)
@ -44,7 +46,7 @@ See the documentation to initialize your shell: https://ohmyposh.dev/docs/instal
}
)
func init() { //nolint:gochecknoinits
func init() {
initCmd.Flags().BoolVarP(&printOutput, "print", "p", false, "print the init script")
initCmd.Flags().BoolVarP(&strict, "strict", "s", false, "run in strict mode")
initCmd.Flags().BoolVarP(&manual, "manual", "m", false, "enable/disable manual mode")
@ -63,11 +65,15 @@ func runInit(shellName string) {
}
env.Init()
defer env.Close()
cfg := engine.LoadConfig(env)
shell.Transient = cfg.TransientPrompt != nil
shell.ErrorLine = cfg.ErrorLine != nil || cfg.ValidLine != nil
shell.Tooltips = len(cfg.Tooltips) > 0
shell.ShellIntegration = cfg.ShellIntegration
shell.PromptMark = shellName == shell.FISH && cfg.ITermFeatures != nil && cfg.ITermFeatures.Contains(ansi.PromptMark)
for i, block := range cfg.Blocks {
// only fetch cursor position when relevant
if !cfg.DisableCursorPositioning && (i == 0 && block.Newline) {
@ -77,11 +83,18 @@ func runInit(shellName string) {
shell.RPrompt = true
}
}
// allow overriding the upgrade notice from the config
if cfg.DisableNotice {
env.Cache().Set(upgrade.CACHEKEY, "disabled", -1)
}
if printOutput {
init := shell.PrintInit(env)
fmt.Print(init)
return
}
init := shell.Init(env)
fmt.Print(init)
}

View file

@ -27,6 +27,6 @@ var noticeCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(noticeCmd)
}

View file

@ -94,7 +94,7 @@ var printCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
printCmd.Flags().StringVar(&pwd, "pwd", "", "current working directory")
printCmd.Flags().StringVar(&pswd, "pswd", "", "current working directory (according to pwsh)")
printCmd.Flags().StringVar(&shellName, "shell", "", "the shell to print for")

View file

@ -15,7 +15,7 @@ var promptCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
// legacy support
promptCmd.AddCommand(initCmd)
promptCmd.AddCommand(debugCmd)

View file

@ -36,7 +36,8 @@ on getting started, have a look at the docs at https://ohmyposh.dev`,
func Execute() {
if err := RootCmd.Execute(); err != nil {
os.Exit(1)
// software error
os.Exit(70)
}
}
@ -46,7 +47,7 @@ var (
initialize bool
)
func init() { //nolint:gochecknoinits
func init() {
RootCmd.PersistentFlags().StringVarP(&config, "config", "c", "", "config file path")
RootCmd.Flags().BoolVarP(&initialize, "init", "i", false, "init (deprecated)")
RootCmd.Flags().BoolVar(&displayVersion, "version", false, "version")

View file

@ -49,6 +49,6 @@ var toggleCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
RootCmd.AddCommand(toggleCmd)
}

View file

@ -27,7 +27,7 @@ var versionCmd = &cobra.Command{
},
}
func init() { //nolint:gochecknoinits
func init() {
versionCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "write verbose output")
RootCmd.AddCommand(versionCmd)
}

View file

@ -38,19 +38,19 @@ const (
// Block defines a part of the prompt with optional segments
type Block struct {
Type BlockType `json:"type,omitempty"`
Alignment BlockAlignment `json:"alignment,omitempty"`
Segments []*Segment `json:"segments,omitempty"`
Newline bool `json:"newline,omitempty"`
Filler string `json:"filler,omitempty"`
Overflow Overflow `json:"overflow,omitempty"`
Type BlockType `json:"type,omitempty" toml:"type,omitempty"`
Alignment BlockAlignment `json:"alignment,omitempty" toml:"alignment,omitempty"`
Segments []*Segment `json:"segments,omitempty" toml:"segments,omitempty"`
Newline bool `json:"newline,omitempty" toml:"newline,omitempty"`
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
Overflow Overflow `json:"overflow,omitempty" toml:"overflow,omitempty"`
// Deprecated: keep the logic for legacy purposes
HorizontalOffset int `json:"horizontal_offset,omitempty"`
VerticalOffset int `json:"vertical_offset,omitempty"`
HorizontalOffset int `json:"horizontal_offset,omitempty" toml:"horizontal_offset,omitempty"`
VerticalOffset int `json:"vertical_offset,omitempty" toml:"vertical_offset,omitempty"`
MaxWidth int `json:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty"`
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
env platform.Environment
writer *ansi.Writer
@ -176,38 +176,70 @@ func (b *Block) writeSeparator(final bool) {
if isPreviousDiamond {
b.adjustTrailingDiamondColorOverrides()
}
if isPreviousDiamond && isCurrentDiamond && len(b.activeSegment.LeadingDiamond) == 0 {
b.writer.Write(ansi.Background, ansi.ParentBackground, b.previousActiveSegment.TrailingDiamond)
return
}
if isPreviousDiamond && len(b.previousActiveSegment.TrailingDiamond) > 0 {
b.writer.Write(ansi.Transparent, ansi.ParentBackground, b.previousActiveSegment.TrailingDiamond)
}
resolvePowerlineSymbol := func() string {
var symbol string
if b.activeSegment.isPowerline() {
symbol = b.activeSegment.PowerlineSymbol
} else if b.previousActiveSegment != nil && b.previousActiveSegment.isPowerline() {
symbol = b.previousActiveSegment.PowerlineSymbol
isPowerline := b.activeSegment.isPowerline()
shouldOverridePowerlineLeadingSymbol := func() bool {
if !isPowerline {
return false
}
return symbol
if isPowerline && len(b.activeSegment.LeadingPowerlineSymbol) == 0 {
return false
}
if b.previousActiveSegment != nil && b.previousActiveSegment.isPowerline() {
return false
}
return true
}
if shouldOverridePowerlineLeadingSymbol() {
b.writer.Write(ansi.Transparent, ansi.Background, b.activeSegment.LeadingPowerlineSymbol)
return
}
resolvePowerlineSymbol := func() string {
if isPowerline {
return b.activeSegment.PowerlineSymbol
}
if b.previousActiveSegment != nil && b.previousActiveSegment.isPowerline() {
return b.previousActiveSegment.PowerlineSymbol
}
return ""
}
symbol := resolvePowerlineSymbol()
if len(symbol) == 0 {
return
}
bgColor := ansi.Background
if final || !b.activeSegment.isPowerline() {
if final || !isPowerline {
bgColor = ansi.Transparent
}
if b.activeSegment.style() == Diamond && len(b.activeSegment.LeadingDiamond) == 0 {
bgColor = ansi.Background
}
if b.activeSegment.InvertPowerline {
b.writer.Write(b.getPowerlineColor(), bgColor, symbol)
return
}
b.writer.Write(bgColor, b.getPowerlineColor(), symbol)
}

View file

@ -2,7 +2,6 @@ package engine
import (
"bytes"
json2 "encoding/json"
"fmt"
"io"
"os"
@ -10,18 +9,16 @@ import (
"strings"
"time"
json "github.com/goccy/go-json"
yaml "github.com/goccy/go-yaml"
"github.com/gookit/goutil/jsonutil"
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/jandedobbeleer/oh-my-posh/src/segments"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/gookit/config/v2"
"github.com/gookit/config/v2/json"
"github.com/gookit/config/v2/toml"
yaml "github.com/gookit/config/v2/yamlv3"
"github.com/mitchellh/mapstructure"
toml "github.com/pelletier/go-toml/v2"
)
const (
@ -34,33 +31,35 @@ const (
// Config holds all the theme for rendering the prompt
type Config struct {
Version int `json:"version"`
FinalSpace bool `json:"final_space,omitempty"`
ConsoleTitleTemplate string `json:"console_title_template,omitempty"`
TerminalBackground string `json:"terminal_background,omitempty"`
AccentColor string `json:"accent_color,omitempty"`
Blocks []*Block `json:"blocks,omitempty"`
Tooltips []*Segment `json:"tooltips,omitempty"`
TransientPrompt *Segment `json:"transient_prompt,omitempty"`
ValidLine *Segment `json:"valid_line,omitempty"`
ErrorLine *Segment `json:"error_line,omitempty"`
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty"`
DebugPrompt *Segment `json:"debug_prompt,omitempty"`
Palette ansi.Palette `json:"palette,omitempty"`
Palettes *ansi.Palettes `json:"palettes,omitempty"`
Cycle ansi.Cycle `json:"cycle,omitempty"`
ShellIntegration bool `json:"shell_integration,omitempty"`
PWD string `json:"pwd,omitempty"`
Var map[string]any `json:"var,omitempty"`
DisableCursorPositioning bool `json:"disable_cursor_positioning,omitempty"`
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty"`
Version int `json:"version" toml:"version"`
FinalSpace bool `json:"final_space,omitempty" toml:"final_space,omitempty"`
ConsoleTitleTemplate string `json:"console_title_template,omitempty" toml:"console_title_template,omitempty"`
TerminalBackground string `json:"terminal_background,omitempty" toml:"terminal_background,omitempty"`
AccentColor string `json:"accent_color,omitempty" toml:"accent_color,omitempty"`
Blocks []*Block `json:"blocks,omitempty" toml:"blocks,omitempty"`
Tooltips []*Segment `json:"tooltips,omitempty" toml:"tooltips,omitempty"`
TransientPrompt *Segment `json:"transient_prompt,omitempty" toml:"transient_prompt,omitempty"`
ValidLine *Segment `json:"valid_line,omitempty" toml:"valid_line,omitempty"`
ErrorLine *Segment `json:"error_line,omitempty" toml:"error_line,omitempty"`
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty" toml:"secondary_prompt,omitempty"`
DebugPrompt *Segment `json:"debug_prompt,omitempty" toml:"debug_prompt,omitempty"`
Palette ansi.Palette `json:"palette,omitempty" toml:"palette,omitempty"`
Palettes *ansi.Palettes `json:"palettes,omitempty" toml:"palettes,omitempty"`
Cycle ansi.Cycle `json:"cycle,omitempty" toml:"cycle,omitempty"`
ShellIntegration bool `json:"shell_integration,omitempty" toml:"shell_integration,omitempty"`
PWD string `json:"pwd,omitempty" toml:"pwd,omitempty"`
Var map[string]any `json:"var,omitempty" toml:"var,omitempty"`
DisableCursorPositioning bool `json:"disable_cursor_positioning,omitempty" toml:"disable_cursor_positioning,omitempty"`
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty" toml:"patch_pwsh_bleed,omitempty"`
DisableNotice bool `json:"disable_notice,omitempty" toml:"disable_notice,omitempty"`
ITermFeatures ansi.ITermFeatures `json:"iterm_features,omitempty" toml:"iterm_features,omitempty"`
// Deprecated
OSC99 bool `json:"osc99,omitempty"`
OSC99 bool `json:"osc99,omitempty" toml:"osc99,omitempty"`
Output string `json:"-"`
MigrateGlyphs bool `json:"-"`
Format string `json:"-"`
Output string `json:"-" toml:"-"`
MigrateGlyphs bool `json:"-" toml:"-"`
Format string `json:"-" toml:"-"`
origin string
// eval bool
@ -135,94 +134,80 @@ func loadConfig(env platform.Environment) *Config {
cfg.Format = strings.TrimPrefix(filepath.Ext(configFile), ".")
cfg.env = env
// support different extensions
switch cfg.Format {
case "yml":
cfg.Format = YAML
case "jsonc":
cfg.Format = JSON
}
config.AddDriver(yaml.Driver.WithAliases("yaml", "yml"))
config.AddDriver(json.Driver.WithAliases("json", "jsonc"))
config.AddDriver(toml.Driver)
if config.Default().IsEmpty() {
config.WithOptions(func(opt *config.Options) {
opt.DecoderConfig = &mapstructure.DecoderConfig{
TagName: "json",
}
})
}
err := config.LoadFiles(configFile)
data, err := os.ReadFile(configFile)
if err != nil {
env.Error(err)
env.DebugF("error reading config file: %s", err)
return defaultConfig(env, true)
}
err = config.BindStruct("", &cfg)
switch cfg.Format {
case "yml", "yaml":
cfg.Format = YAML
err = yaml.Unmarshal(data, &cfg)
case "jsonc", "json":
cfg.Format = JSON
str := jsonutil.StripComments(string(data))
data = []byte(str)
decoder := json.NewDecoder(bytes.NewReader(data))
err = decoder.Decode(&cfg)
case "toml", "tml":
cfg.Format = TOML
err = toml.Unmarshal(data, &cfg)
default:
err = fmt.Errorf("unsupported config file format: %s", cfg.Format)
}
if err != nil {
env.Error(err)
env.DebugF("error decoding config file: %s", err)
return defaultConfig(env, true)
}
return &cfg
}
func (cfg *Config) sync() {
if !cfg.updated {
return
}
var structMap map[string]any
inrec, err := json2.Marshal(cfg)
if err != nil {
return
}
err = json2.Unmarshal(inrec, &structMap)
if err != nil {
return
}
// remove empty structs
for k, v := range structMap {
if smap, OK := v.(map[string]any); OK && len(smap) == 0 {
delete(structMap, k)
}
}
config.SetData(structMap)
}
func (cfg *Config) Export(format string) string {
cfg.sync()
if len(format) != 0 {
cfg.Format = format
}
config.AddDriver(yaml.Driver)
config.AddDriver(toml.Driver)
var result bytes.Buffer
if cfg.Format == JSON {
jsonEncoder := json2.NewEncoder(&result)
switch cfg.Format {
case YAML:
prefix := "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
yamlEncoder := yaml.NewEncoder(&result)
err := yamlEncoder.Encode(cfg)
if err != nil {
return ""
}
return prefix + result.String()
case JSON:
jsonEncoder := json.NewEncoder(&result)
jsonEncoder.SetEscapeHTML(false)
jsonEncoder.SetIndent("", " ")
_ = jsonEncoder.Encode(cfg)
prefix := "{\n \"$schema\": \"https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\","
data := strings.Replace(result.String(), "{", prefix, 1)
return escapeGlyphs(data, cfg.MigrateGlyphs)
case TOML:
prefix := "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
tomlEncoder := toml.NewEncoder(&result)
tomlEncoder.SetIndentTables(true)
err := tomlEncoder.Encode(cfg)
if err != nil {
return ""
}
return prefix + result.String()
}
_, _ = config.DumpTo(&result, cfg.Format)
var prefix string
switch cfg.Format {
case YAML:
prefix = "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
case TOML:
prefix = "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
}
return prefix + escapeGlyphs(result.String(), cfg.MigrateGlyphs)
// unsupported format
return ""
}
func (cfg *Config) BackupAndMigrate() {
@ -233,19 +218,30 @@ func (cfg *Config) BackupAndMigrate() {
func (cfg *Config) Write(format string) {
content := cfg.Export(format)
if len(content) == 0 {
// we are unable to perform the export
os.Exit(65)
return
}
destination := cfg.Output
if len(destination) == 0 {
destination = cfg.origin
}
f, err := os.OpenFile(destination, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
f, err := os.OpenFile(destination, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return
}
defer func() {
_ = f.Close()
}()
_, err = f.WriteString(content)
if err != nil {
return
}
_ = f.Close()
}
func (cfg *Config) Backup() {

View file

@ -6,46 +6,12 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
"github.com/jandedobbeleer/oh-my-posh/src/mock"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/segments"
"github.com/gookit/config/v2"
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)
func testClearDefaultConfig() {
config.Default().ClearAll()
}
func TestParseMappedLocations(t *testing.T) {
defer testClearDefaultConfig()
cases := []struct {
Case string
JSON string
}{
{Case: "new format", JSON: `{ "properties": { "mapped_locations": {"folder1": "one","folder2": "two"} } }`},
{Case: "old format", JSON: `{ "properties": { "mapped_locations": [["folder1", "one"], ["folder2", "two"]] } }`},
}
for _, tc := range cases {
config.ClearAll()
config.WithOptions(func(opt *config.Options) {
opt.DecoderConfig = &mapstructure.DecoderConfig{
TagName: "config",
}
})
err := config.LoadStrings(config.JSON, tc.JSON)
assert.NoError(t, err)
var segment Segment
err = config.BindStruct("", &segment)
assert.NoError(t, err)
mappedLocations := segment.Properties.GetKeyValueMap(segments.MappedLocations, make(map[string]string))
assert.Equal(t, "two", mappedLocations["folder2"])
}
}
func TestEscapeGlyphs(t *testing.T) {
defer testClearDefaultConfig()
cases := []struct {
Input string
Expected string

View file

@ -132,6 +132,10 @@ func (e *Engine) isWarp() bool {
return e.Env.Getenv("TERM_PROGRAM") == "WarpTerminal"
}
func (e *Engine) isIterm() bool {
return e.Env.Getenv("TERM_PROGRAM") == "iTerm.app"
}
func (e *Engine) shouldFill(filler string, remaining, blockLength int) (string, bool) {
if len(filler) == 0 {
return "", false
@ -222,14 +226,6 @@ func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
return false
}
// in ZSH, RPROMPT is printed with a trailing space
// to ensure alignment, we need to print a space here
// see https://github.com/JanDeDobbeleer/oh-my-posh/issues/4327
if e.Env.Shell() == shell.ZSH {
text += " "
length++
}
space, OK := e.canWriteRightBlock(false)
// we can't print the right block as there's not enough room available
if !OK {

View file

@ -113,7 +113,6 @@ func engineRender() {
defer env.Close()
cfg := LoadConfig(env)
defer testClearDefaultConfig()
writerColors := cfg.MakeColors()
writer := &ansi.Writer{

View file

@ -42,8 +42,8 @@ import (
"github.com/esimov/stackblur-go"
"github.com/fogleman/gg"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
)
type ConnectionError struct {
@ -217,7 +217,7 @@ func (ir *ImageRenderer) loadFonts() error {
// Download font if not cached
if data == nil {
url := "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.1.0/Hack.zip"
url := "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/Hack.zip"
var err error
data, err = fontCLI.Download(url)
@ -237,7 +237,7 @@ func (ir *ImageRenderer) loadFonts() error {
return err
}
fontFaceOptions := &truetype.Options{Size: 2.0 * 12, DPI: 144}
fontFaceOptions := &opentype.FaceOptions{Size: 2.0 * 12, DPI: 144}
parseFont := func(file *zip.File) (font.Face, error) {
rc, err := file.Open()
@ -252,12 +252,16 @@ func (ir *ImageRenderer) loadFonts() error {
return nil, err
}
font, err := truetype.Parse(data)
font, err := opentype.Parse(data)
if err != nil {
return nil, err
}
return truetype.NewFace(font, fontFaceOptions), nil
fontFace, err := opentype.NewFace(font, fontFaceOptions)
if err != nil {
return nil, err
}
return fontFace, nil
}
for _, file := range zipReader.File {

View file

@ -2,9 +2,10 @@ package engine
import (
"context"
"encoding/csv"
"io"
"net/http"
"strconv"
"strings"
"time"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
@ -30,23 +31,29 @@ func getGlyphCodePoints() (codePoints, error) {
defer response.Body.Close()
lines, err := csv.NewReader(response.Body).ReadAll()
bytes, err := io.ReadAll(response.Body)
if err != nil {
return codePoints, err
}
lines := strings.Split(string(bytes), "\n")
for _, line := range lines {
if len(line) < 2 {
fields := strings.Split(line, ",")
if len(fields) < 2 {
continue
}
oldGlyph, err := strconv.ParseUint(line[0], 16, 32)
oldGlyph, err := strconv.ParseUint(fields[0], 16, 32)
if err != nil {
continue
}
newGlyph, err := strconv.ParseUint(line[1], 16, 32)
newGlyph, err := strconv.ParseUint(fields[1], 16, 32)
if err != nil {
continue
}
codePoints[oldGlyph] = newGlyph
}

View file

@ -68,6 +68,11 @@ func (e *Engine) Primary() string {
e.currentLineLength++
}
if e.Config.ITermFeatures != nil && e.isIterm() {
host, _ := e.Env.Host()
e.write(e.Writer.RenderItermFeatures(e.Config.ITermFeatures, e.Env.Shell(), e.Env.Pwd(), e.Env.User(), host))
}
if e.Config.ShellIntegration && e.Config.TransientPrompt == nil {
e.write(e.Writer.CommandStart())
}

View file

@ -20,28 +20,29 @@ import (
// Segment represent a single segment and it's configuration
type Segment struct {
Type SegmentType `json:"type,omitempty"`
Tips []string `json:"tips,omitempty"`
Style SegmentStyle `json:"style,omitempty"`
PowerlineSymbol string `json:"powerline_symbol,omitempty"`
InvertPowerline bool `json:"invert_powerline,omitempty"`
Foreground string `json:"foreground,omitempty"`
ForegroundTemplates template.List `json:"foreground_templates,omitempty"`
Background string `json:"background,omitempty"`
BackgroundTemplates template.List `json:"background_templates,omitempty"`
LeadingDiamond string `json:"leading_diamond,omitempty"`
TrailingDiamond string `json:"trailing_diamond,omitempty"`
Template string `json:"template,omitempty"`
Templates template.List `json:"templates,omitempty"`
TemplatesLogic template.Logic `json:"templates_logic,omitempty"`
Properties properties.Map `json:"properties,omitempty"`
Interactive bool `json:"interactive,omitempty"`
Alias string `json:"alias,omitempty"`
MaxWidth int `json:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty"`
Filler string `json:"filler,omitempty"`
Type SegmentType `json:"type,omitempty" toml:"type,omitempty"`
Tips []string `json:"tips,omitempty" toml:"tips,omitempty"`
Style SegmentStyle `json:"style,omitempty" toml:"style,omitempty"`
PowerlineSymbol string `json:"powerline_symbol,omitempty" toml:"powerline_symbol,omitempty"`
LeadingPowerlineSymbol string `json:"leading_powerline_symbol,omitempty" toml:"leading_powerline_symbol,omitempty"`
InvertPowerline bool `json:"invert_powerline,omitempty" toml:"invert_powerline,omitempty"`
Foreground string `json:"foreground,omitempty" toml:"foreground,omitempty"`
ForegroundTemplates template.List `json:"foreground_templates,omitempty" toml:"foreground_templates,omitempty"`
Background string `json:"background,omitempty" toml:"background,omitempty"`
BackgroundTemplates template.List `json:"background_templates,omitempty" toml:"background_templates,omitempty"`
LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"`
TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"`
Template string `json:"template,omitempty" toml:"template,omitempty"`
Templates template.List `json:"templates,omitempty" toml:"templates,omitempty"`
TemplatesLogic template.Logic `json:"templates_logic,omitempty" toml:"templates_logic,omitempty"`
Properties properties.Map `json:"properties,omitempty" toml:"properties,omitempty"`
Interactive bool `json:"interactive,omitempty" toml:"interactive,omitempty"`
Alias string `json:"alias,omitempty" toml:"alias,omitempty"`
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
Enabled bool `json:"-"`
Enabled bool `json:"-" toml:"-"`
colors *ansi.Colors
env platform.Environment
@ -146,6 +147,8 @@ const (
FOSSIL SegmentType = "fossil"
// GCP writes the active GCP context
GCP SegmentType = "gcp"
// FIREBASE writes the active firebase project
FIREBASE SegmentType = "firebase"
// GIT represents the git status and information
GIT SegmentType = "git"
// GITVERSION represents the gitversion information
@ -158,8 +161,6 @@ const (
HELM SegmentType = "helm"
// IPIFY segment
IPIFY SegmentType = "ipify"
// ITERM inserts the Shell Integration prompt mark on iTerm zsh/bash/fish
ITERM SegmentType = "iterm"
// JAVA writes the active java version
JAVA SegmentType = "java"
// JULIA writes which julia version is currently active
@ -202,6 +203,8 @@ const (
PLASTIC SegmentType = "plastic"
// Project version
PROJECT SegmentType = "project"
// PULUMI writes the pulumi user, store and stack
PULUMI SegmentType = "pulumi"
// PYTHON writes the virtual env name
PYTHON SegmentType = "python"
// QUASAR writes the QUASAR version and context
@ -236,6 +239,8 @@ const (
SWIFT SegmentType = "swift"
// SYSTEMINFO writes system information (memory, cpu, load)
SYSTEMINFO SegmentType = "sysinfo"
// TALOSCTL writes the talosctl context
TALOSCTL SegmentType = "talosctl"
// TERRAFORM writes the terraform workspace we're currently in
TERRAFORM SegmentType = "terraform"
// TEXT writes a text
@ -294,13 +299,13 @@ var Segments = map[SegmentType]func() SegmentWriter{
FLUTTER: func() SegmentWriter { return &segments.Flutter{} },
FOSSIL: func() SegmentWriter { return &segments.Fossil{} },
GCP: func() SegmentWriter { return &segments.Gcp{} },
FIREBASE: func() SegmentWriter { return &segments.Firebase{} },
GIT: func() SegmentWriter { return &segments.Git{} },
GITVERSION: func() SegmentWriter { return &segments.GitVersion{} },
GOLANG: func() SegmentWriter { return &segments.Golang{} },
HASKELL: func() SegmentWriter { return &segments.Haskell{} },
HELM: func() SegmentWriter { return &segments.Helm{} },
IPIFY: func() SegmentWriter { return &segments.IPify{} },
ITERM: func() SegmentWriter { return &segments.ITerm{} },
JAVA: func() SegmentWriter { return &segments.Java{} },
JULIA: func() SegmentWriter { return &segments.Julia{} },
KOTLIN: func() SegmentWriter { return &segments.Kotlin{} },
@ -322,6 +327,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
PHP: func() SegmentWriter { return &segments.Php{} },
PLASTIC: func() SegmentWriter { return &segments.Plastic{} },
PROJECT: func() SegmentWriter { return &segments.Project{} },
PULUMI: func() SegmentWriter { return &segments.Pulumi{} },
PYTHON: func() SegmentWriter { return &segments.Python{} },
QUASAR: func() SegmentWriter { return &segments.Quasar{} },
R: func() SegmentWriter { return &segments.R{} },
@ -339,6 +345,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
SVN: func() SegmentWriter { return &segments.Svn{} },
SWIFT: func() SegmentWriter { return &segments.Swift{} },
SYSTEMINFO: func() SegmentWriter { return &segments.SystemInfo{} },
TALOSCTL: func() SegmentWriter { return &segments.TalosCTL{} },
TERRAFORM: func() SegmentWriter { return &segments.Terraform{} },
TEXT: func() SegmentWriter { return &segments.Text{} },
TIME: func() SegmentWriter { return &segments.Time{} },

View file

@ -98,12 +98,13 @@ func (m *main) buildFontList(nerdFonts []*Asset) {
}
func getFontsList() {
nerdFonts, err := Nerds()
fonts, err := Fonts()
if err != nil {
program.Send(errMsg(err))
return
}
program.Send(loadMsg(nerdFonts))
program.Send(loadMsg(fonts))
}
func downloadFontZip(location string) {
@ -272,6 +273,6 @@ func Run(font string, system bool) {
program = tea.NewProgram(main)
if _, err := program.Run(); err != nil {
print("Error running program: %v", err)
os.Exit(1)
os.Exit(70)
}
}

View file

@ -4,7 +4,9 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"sort"
"strings"
"time"
@ -23,30 +25,57 @@ type Asset struct {
func (a Asset) FilterValue() string { return a.Name }
func Nerds() ([]*Asset, error) {
ctx, cancelF := context.WithTimeout(context.Background(), time.Second*time.Duration(20))
defer cancelF()
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest", nil)
func Fonts() ([]*Asset, error) {
assets, err := fetchFontAssets("ryanoasis/nerd-fonts")
if err != nil {
return nil, err
}
cascadiaCode, err := fetchFontAssets("microsoft/cascadia-code")
if err != nil {
return assets, nil
}
assets = append(assets, cascadiaCode...)
sort.Slice(assets, func(i, j int) bool { return assets[i].Name < assets[j].Name })
return assets, nil
}
func CascadiaCode() ([]*Asset, error) {
return fetchFontAssets("microsoft/cascadia-code")
}
func fetchFontAssets(repo string) ([]*Asset, error) {
ctx, cancelF := context.WithTimeout(context.Background(), time.Second*time.Duration(20))
defer cancelF()
repoURL := "https://api.github.com/repos/" + repo + "/releases/latest"
req, err := http.NewRequestWithContext(ctx, "GET", repoURL, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/vnd.github.v3+json")
response, err := platform.Client.Do(req)
if err != nil || response.StatusCode != http.StatusOK {
return nil, errors.New("failed to get nerd fonts release")
return nil, fmt.Errorf("failed to get %s release", repo)
}
defer response.Body.Close()
var release release
err = json.NewDecoder(response.Body).Decode(&release)
if err != nil {
return nil, errors.New("failed to parse nerd fonts release")
}
var nerdFonts []*Asset
var fonts []*Asset
for _, asset := range release.Assets {
if asset.State == "uploaded" && strings.HasSuffix(asset.Name, ".zip") {
asset.Name = strings.TrimSuffix(asset.Name, ".zip")
nerdFonts = append(nerdFonts, asset)
fonts = append(fonts, asset)
}
}
return nerdFonts, nil
return fonts, nil
}

View file

@ -1,57 +1,56 @@
module github.com/jandedobbeleer/oh-my-posh/src
go 1.21
go 1.22.3
require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161
github.com/Masterminds/sprig/v3 v3.2.3
github.com/alecthomas/assert v1.0.0
github.com/alecthomas/colour v0.1.0 // indirect
github.com/alecthomas/repr v0.2.0 // indirect
github.com/alecthomas/repr v0.4.0 // indirect
github.com/esimov/stackblur-go v1.1.0
github.com/fogleman/gg v1.3.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4
github.com/gookit/config/v2 v2.2.5
github.com/gookit/goutil v0.6.15 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shirou/gopsutil/v3 v3.24.1
github.com/stretchr/objx v0.5.1 // indirect
github.com/stretchr/testify v1.8.4
github.com/shirou/gopsutil/v3 v3.24.4
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.9.0
github.com/wayneashleyberry/terminal-dimensions v1.1.0
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/image v0.15.0
golang.org/x/sys v0.17.0
golang.org/x/text v0.14.0
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/image v0.16.0
golang.org/x/sys v0.20.0
golang.org/x/text v0.15.0
gopkg.in/ini.v1 v1.67.0
)
require (
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/hashicorp/hcl/v2 v2.20.0
github.com/charmbracelet/bubbletea v0.26.2
github.com/charmbracelet/lipgloss v0.10.0
github.com/goccy/go-json v0.10.3
github.com/goccy/go-yaml v1.11.3
github.com/gookit/goutil v0.6.15
github.com/hashicorp/hcl/v2 v2.20.1
github.com/mattn/go-runewidth v0.0.15
github.com/pelletier/go-toml/v2 v2.2.2
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3
golang.org/x/mod v0.15.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/mod v0.17.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/BurntSushi/toml v1.3.2
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -62,17 +61,15 @@ require (
)
require (
dario.cat/mergo v1.0.0 // indirect
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.11.3 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
@ -83,16 +80,16 @@ require (
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
github.com/sahilm/fuzzy v0.1.1 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zclconf/go-cty v1.14.2 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/tools v0.17.0 // indirect
github.com/zclconf/go-cty v1.14.3 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/tools v0.19.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
)
@ -100,4 +97,4 @@ replace github.com/atotto/clipboard v0.1.4 => github.com/jandedobbeleer/clipboar
replace github.com/shirou/gopsutil/v3 v3.23.9 => github.com/jandedobbeleer/gopsutil/v3 v3.23.9-1
replace github.com/goccy/go-yaml v1.10.0 => github.com/jandedobbeleer/go-yaml v1.10.0-4
replace github.com/goccy/go-yaml v1.11.3 => github.com/jandedobbeleer/go-yaml v1.11.3-2

View file

@ -1,11 +1,7 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab h1:Ew70NL+wL6v9looOiJJthlqA41VzoJS+q9AyjHJe6/g=
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab/go.mod h1:FvHgTMJanm43G7B3MVSjS/jim5ytVqAJNAOpRhnuHJc=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732 h1:0EDePskeF4vNFCk70ATaFHQzjmwXsk+VImnMJttecNU=
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732/go.mod h1:krTLO7JWu6g8RMxG8sl+T1Hf8W93XQacBKJmqFZ2MFY=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@ -21,20 +17,18 @@ github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI
github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY=
github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk=
github.com/alecthomas/colour v0.1.0/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/charmbracelet/bubbletea v0.26.2 h1:Eeb+n75Om9gQ+I6YpbCXQRKHt5Pn4vMwusQpwLiEgJQ=
github.com/charmbracelet/bubbletea v0.26.2/go.mod h1:6I0nZ3YHUrQj7YHIHlM8RySX4ZIthTliMY+W8X8b+Gs=
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -42,6 +36,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/esimov/stackblur-go v1.1.0 h1:fwnZJC/7sHFzu4CDMgdJ1QxMN/q3k5MGILuoU4hH6oQ=
github.com/esimov/stackblur-go v1.1.0/go.mod h1:7PcTPCHHKStxbZvBkUlQJjRclqjnXtQ0NoORZt1AlHE=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
@ -61,10 +57,8 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -76,14 +70,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU=
github.com/gookit/config/v2 v2.2.5/go.mod h1:NeX+yiNYn6Ei10eJvCQFXuHEPIE/IPS8bqaFIsszzaM=
github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY=
github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E=
github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4=
github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4=
github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk=
github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc=
github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
@ -94,6 +84,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jandedobbeleer/clipboard v0.1.4-1 h1:rJehm5W0a3hvjcxyB3snqLBV4yvMBBc12JyMP7ngNQw=
github.com/jandedobbeleer/clipboard v0.1.4-1/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/jandedobbeleer/go-yaml v1.11.3-2 h1:mz6E/lRl29RqwYkb4HC03C2YwF5tVDNdfiPWa1hYRdo=
github.com/jandedobbeleer/go-yaml v1.11.3-2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -110,8 +102,8 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a h1:3Bm7EwfUQUvhNeKIkUct/gl9eod1TcXuj8stxvi/GoI=
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@ -127,8 +119,6 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@ -140,11 +130,13 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
@ -152,12 +144,12 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5m7QI=
github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU=
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@ -177,16 +169,16 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
@ -199,37 +191,39 @@ github.com/wayneashleyberry/terminal-dimensions v1.1.0/go.mod h1:2lc/0eWCObmhRcz
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zclconf/go-cty v1.14.2 h1:kTG7lqmBou0Zkx35r6HJHUQTvaRPr5bIAf3AoHS0izI=
github.com/zclconf/go-cty v1.14.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty v1.14.3 h1:1JXy1XroaGrzZuG6X9dt7HL6s9AwbY+l4UNL8o5B6ho=
github.com/zclconf/go-cty v1.14.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo=
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -238,26 +232,26 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=

View file

@ -144,9 +144,11 @@ func systemGet(idx int) (*battery, error) {
2|16, // DIGCF_PRESENT|DIGCF_DEVICEINTERFACE
0, 0,
)
if err != nil {
return nil, err
}
defer func() {
_, _, _ = syscall.Syscall(setupDiDestroyDeviceInfoList.Addr(), 1, hdev, 0, 0) //nolint:staticcheck
}()
@ -163,12 +165,15 @@ func systemGet(idx int) (*battery, error) {
uintptr(unsafe.Pointer(&did)),
0,
)
if errno == 259 { // ERROR_NO_MORE_ITEMS
return nil, ErrNotFound
}
if errno != 0 {
return nil, errno
}
var cbRequired uint32
errno = setupDiCall(
setupDiGetDeviceInterfaceDetailW,
@ -180,9 +185,11 @@ func systemGet(idx int) (*battery, error) {
uintptr(unsafe.Pointer(&cbRequired)),
0,
)
if errno != 0 && errno != 122 { // ERROR_INSUFFICIENT_BUFFER
return nil, errno
}
// The god damn struct with ANYSIZE_ARRAY of utf16 in it is crazy.
// So... let's emulate it with array of uint16 ;-D.
// Keep in mind that the first two elements are actually cbSize.
@ -193,6 +200,7 @@ func systemGet(idx int) (*battery, error) {
} else {
*cbSize = 6
}
errno = setupDiCall(
setupDiGetDeviceInterfaceDetailW,
6,
@ -203,9 +211,11 @@ func systemGet(idx int) (*battery, error) {
uintptr(unsafe.Pointer(&cbRequired)),
0,
)
if errno != 0 {
return nil, errno
}
devicePath := &didd[2:][0]
handle, err := windows.CreateFile(
@ -217,9 +227,11 @@ func systemGet(idx int) (*battery, error) {
windows.FILE_ATTRIBUTE_NORMAL,
0,
)
if err != nil {
return nil, err
}
defer func() {
_ = windows.CloseHandle(handle)
}()
@ -238,9 +250,11 @@ func systemGet(idx int) (*battery, error) {
&dwOut,
nil,
)
if err != nil {
return nil, err
}
if bqi.BatteryTag == 0 {
return nil, errors.New("BatteryTag not returned")
}
@ -258,6 +272,7 @@ func systemGet(idx int) (*battery, error) {
&dwOut,
nil,
)
if err != nil {
return nil, err
}
@ -276,6 +291,7 @@ func systemGet(idx int) (*battery, error) {
&dwOut,
nil,
)
if err != nil {
return nil, err
}
@ -283,9 +299,11 @@ func systemGet(idx int) (*battery, error) {
if b.Current, err = uint32ToFloat64(bs.Capacity); err != nil {
return nil, err
}
if b.Voltage, err = uint32ToFloat64(bs.Voltage); err != nil {
return nil, err
}
b.Voltage /= 1000
b.State = readState(bs.PowerState)

View file

@ -37,7 +37,7 @@ func mapMostLogicalState(currentState, newState State) State {
return newState
}
// GetAll returns information about all batteries in the system.
// Get returns information about all batteries in the system.
//
// If error != nil, it will be either ErrFatal or Errors.
// If error is of type Errors, it is guaranteed that length of both returned slices is the same and that i-th error coresponds with i-th battery structure.

View file

@ -2,15 +2,46 @@ package platform
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"sync"
"time"
)
type Cache interface {
Init(home string)
Close()
// Gets the value for a given key.
// Returns the value and a boolean indicating if the key was found.
// In case the ttl expired, the function returns false.
Get(key string) (string, bool)
// Sets a value for a given key.
// The ttl indicates how many minutes to cache the value.
Set(key, value string, ttl int)
// Deletes a key from the cache.
Delete(key string)
}
const (
CacheFile = "/omp.cache"
)
func pid() string {
pid := os.Getenv("POSH_PID")
if len(pid) == 0 {
pid = strconv.Itoa(os.Getppid())
}
return pid
}
var (
TEMPLATECACHE = fmt.Sprintf("template_cache_%s", pid())
TOGGLECACHE = fmt.Sprintf("toggle_cache_%s", pid())
PROMPTCOUNTCACHE = fmt.Sprintf("prompt_count_cache_%s", pid())
)
type cacheObject struct {
Value string `json:"value"`
Timestamp int64 `json:"timestamp"`
@ -91,3 +122,50 @@ func (fc *fileCache) Delete(key string) {
fc.cache.Delete(key)
fc.dirty = true
}
type commandCache struct {
commands *ConcurrentMap
}
func (c *commandCache) set(command, path string) {
c.commands.Set(command, path)
}
func (c *commandCache) get(command string) (string, bool) {
cacheCommand, found := c.commands.Get(command)
if !found {
return "", false
}
command, ok := cacheCommand.(string)
return command, ok
}
type TemplateCache struct {
Root bool
PWD string
Folder string
Shell string
ShellVersion string
UserName string
HostName string
Code int
Env map[string]string
Var SimpleMap
OS string
WSL bool
PromptCount int
SHLVL int
Segments *ConcurrentMap
SegmentsCache SimpleMap
initialized bool
sync.RWMutex
}
func (t *TemplateCache) AddSegmentData(key string, value any) {
t.Segments.Set(key, value)
}
func (t *TemplateCache) RemoveSegmentData(key string) {
t.Segments.Delete(key)
}

View file

@ -37,20 +37,6 @@ const (
CMD = "cmd"
)
func pid() string {
pid := os.Getenv("POSH_PID")
if len(pid) == 0 {
pid = strconv.Itoa(os.Getppid())
}
return pid
}
var (
TEMPLATECACHE = fmt.Sprintf("template_cache_%s", pid())
TOGGLECACHE = fmt.Sprintf("toggle_cache_%s", pid())
PROMPTCOUNTCACHE = fmt.Sprintf("prompt_count_cache_%s", pid())
)
type Flags struct {
ErrorCode int
PipeStatus string
@ -92,20 +78,6 @@ type FileInfo struct {
IsDir bool
}
type Cache interface {
Init(home string)
Close()
// Gets the value for a given key.
// Returns the value and a boolean indicating if the key was found.
// In case the ttl expired, the function returns false.
Get(key string) (string, bool)
// Sets a value for a given key.
// The ttl indicates how many minutes to cache the value.
Set(key, value string, ttl int)
// Deletes a key from the cache.
Delete(key string)
}
type HTTPRequestModifier func(request *http.Request)
type WindowsRegistryValueType string
@ -168,36 +140,6 @@ type SystemInfo struct {
Disks map[string]disk.IOCountersStat
}
type TemplateCache struct {
Root bool
PWD string
Folder string
Shell string
ShellVersion string
UserName string
HostName string
Code int
Env map[string]string
Var SimpleMap
OS string
WSL bool
PromptCount int
SHLVL int
Segments *ConcurrentMap
SegmentsCache SimpleMap
initialized bool
sync.RWMutex
}
func (t *TemplateCache) AddSegmentData(key string, value any) {
t.Segments.Set(key, value)
}
func (t *TemplateCache) RemoveSegmentData(key string) {
t.Segments.Delete(key)
}
type Environment interface {
Getenv(key string) string
Pwd() string
@ -253,34 +195,20 @@ type Environment interface {
Trace(start time.Time, args ...string)
}
type commandCache struct {
commands *ConcurrentMap
}
func (c *commandCache) set(command, path string) {
c.commands.Set(command, path)
}
func (c *commandCache) get(command string) (string, bool) {
cacheCommand, found := c.commands.Get(command)
if !found {
return "", false
}
command, ok := cacheCommand.(string)
return command, ok
}
type Shell struct {
CmdFlags *Flags
Var SimpleMap
cwd string
host string
cmdCache *commandCache
fileCache *fileCache
tmplCache *TemplateCache
networks []*Connection
sync.RWMutex
lsDirMap ConcurrentMap
}
func (env *Shell) Init() {
@ -430,20 +358,37 @@ func (env *Shell) Pwd() string {
}
func (env *Shell) HasFiles(pattern string) bool {
return env.HasFilesInDir(env.Pwd(), pattern)
}
func (env *Shell) HasFilesInDir(dir, pattern string) bool {
defer env.Trace(time.Now(), pattern)
cwd := env.Pwd()
fileSystem := os.DirFS(cwd)
fileSystem := os.DirFS(dir)
var dirEntries []fs.DirEntry
matches, err := fs.ReadDir(fileSystem, ".")
if err != nil {
env.Error(err)
env.Debug("false")
return false
if files, OK := env.lsDirMap.Get(dir); OK {
dirEntries, _ = files.([]fs.DirEntry)
}
if len(dirEntries) == 0 {
var err error
dirEntries, err = fs.ReadDir(fileSystem, ".")
if err != nil {
env.Error(err)
env.Debug("false")
return false
}
env.lsDirMap.Set(dir, dirEntries)
}
pattern = strings.ToLower(pattern)
for _, match := range matches {
env.RWMutex.RLock()
defer env.RWMutex.RUnlock()
for _, match := range dirEntries {
if match.IsDir() {
continue
}
@ -465,20 +410,6 @@ func (env *Shell) HasFiles(pattern string) bool {
return false
}
func (env *Shell) HasFilesInDir(dir, pattern string) bool {
defer env.Trace(time.Now(), pattern)
fileSystem := os.DirFS(dir)
matches, err := fs.Glob(fileSystem, pattern)
if err != nil {
env.Error(err)
env.Debug("false")
return false
}
hasFilesInDir := len(matches) > 0
env.DebugF("%t", hasFilesInDir)
return hasFilesInDir
}
func (env *Shell) HasFileInParentDirs(pattern string, depth uint) bool {
defer env.Trace(time.Now(), pattern, fmt.Sprint(depth))
currentFolder := env.Pwd()
@ -566,13 +497,20 @@ func (env *Shell) User() string {
func (env *Shell) Host() (string, error) {
defer env.Trace(time.Now())
if len(env.host) != 0 {
return env.host, nil
}
hostName, err := os.Hostname()
if err != nil {
env.Error(err)
return "", err
}
hostName = cleanHostName(hostName)
env.Debug(hostName)
env.host = hostName
return hostName, nil
}

View file

@ -33,8 +33,8 @@ func (c *Bazel) Init(props properties.Properties, env platform.Environment) {
regex: `bazel (?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+)))`,
},
},
// Use the correct URL for Bazel 6.x. it does not have the docs subdomain.
versionURLTemplate: "https://{{ if ne .Major \"6\" }}docs.{{ end }}bazel.build/versions/{{ .Major }}.{{ .Minor }}.{{ .Patch }}",
// Use the correct URL for Bazel >5.4.1, since they do not have the docs subdomain.
versionURLTemplate: "https://{{ if lt .Major 6 }}docs.{{ end }}bazel.build/versions/{{ .Major }}.{{ .Minor }}.{{ .Patch }}",
}
c.Icon = props.GetString(Icon, "\ue63a")
}

View file

@ -14,9 +14,12 @@ func TestBazel(t *testing.T) {
Version string
Template string
}{
{Case: "bazel 4.0.0", ExpectedString: "<LINK>https://docs.bazel.build/versions/4.0.0<TEXT>\ue63a</TEXT></LINK> 4.0.0", Version: "bazel 4.0.0", Template: ""},
{Case: "bazel 5.4.1", ExpectedString: "<LINK>https://docs.bazel.build/versions/5.4.1<TEXT>\ue63a</TEXT></LINK> 5.4.1", Version: "bazel 5.4.1", Template: ""},
{Case: "bazel 6.4.0", ExpectedString: "<LINK>https://bazel.build/versions/6.4.0<TEXT>\ue63a</TEXT></LINK> 6.4.0", Version: "bazel 6.4.0", Template: ""},
{Case: "bazel 10.11.12", ExpectedString: "<LINK>https://docs.bazel.build/versions/3.7.0<TEXT>\ue63a</TEXT></LINK> 3.7.0", Version: "bazel 3.7.0"},
{Case: "", ExpectedString: "\ue63a err parsing info from bazel with", Version: ""},
{Case: "bazel 7.1.1", ExpectedString: "<LINK>https://bazel.build/versions/7.1.1<TEXT>\ue63a</TEXT></LINK> 7.1.1", Version: "bazel 7.1.1", Template: ""},
{Case: "bazel 10.11.12", ExpectedString: "<LINK>https://bazel.build/versions/10.11.12<TEXT>\ue63a</TEXT></LINK> 10.11.12", Version: "bazel 10.11.12", Template: ""},
{Case: "", ExpectedString: "\ue63a err parsing info from bazel with", Version: "", Template: ""},
}
for _, tc := range cases {
params := &mockedLanguageParams{

View file

@ -21,6 +21,8 @@ const (
Command properties.Property = "command"
// Command to execute
Script properties.Property = "script"
// Interpret execution, or not
Interpret properties.Property = "interpret"
)
func (c *Cmd) Template() string {
@ -32,18 +34,28 @@ func (c *Cmd) Enabled() bool {
if !c.env.HasCommand(shell) {
return false
}
command := c.props.GetString(Command, "")
if len(command) != 0 {
return c.runCommand(shell, command)
}
script := c.props.GetString(Script, "")
if len(script) != 0 {
return c.runScript(shell, script)
}
return false
}
func (c *Cmd) runCommand(shell, command string) bool {
interpret := c.props.GetBool(Interpret, true)
if !interpret {
c.Output = c.env.RunShellCommand(shell, command)
return len(c.Output) != 0
}
if strings.Contains(command, "||") {
commands := strings.Split(command, "||")
for _, cmd := range commands {
@ -54,6 +66,7 @@ func (c *Cmd) runCommand(shell, command string) bool {
}
}
}
if strings.Contains(command, "&&") {
var output string
commands := strings.Split(command, "&&")
@ -63,6 +76,7 @@ func (c *Cmd) runCommand(shell, command string) bool {
c.Output = output
return len(c.Output) != 0
}
c.Output = c.env.RunShellCommand(shell, strings.TrimSpace(command))
return len(c.Output) != 0
}

View file

@ -136,6 +136,23 @@ func TestExecuteMultipleCommandsOrDisabled(t *testing.T) {
assert.False(t, enabled)
}
func TestExecuteNonInterpretedCommand(t *testing.T) {
env := new(mock.MockedEnvironment)
env.On("HasCommand", "bash").Return(true)
env.On("RunShellCommand", "bash", "echo hello && echo world").Return("hello world")
props := properties.Map{
Command: "echo hello && echo world",
Interpret: false,
}
c := &Cmd{
props: props,
env: env,
}
enabled := c.Enabled()
assert.True(t, enabled)
assert.Equal(t, "hello world", renderTemplate(env, c.Template(), c))
}
func TestExecuteScript(t *testing.T) {
cases := []struct {
Case string

84
src/segments/firebase.go Normal file
View file

@ -0,0 +1,84 @@
package segments
import (
"encoding/json"
"errors"
"path/filepath"
"strings"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
)
const (
FIREBASENOACTIVECONFIG = "NO ACTIVE CONFIG FOUND"
)
type Firebase struct {
props properties.Properties
env platform.Environment
Project string
}
type FirebaseData struct {
ActiveProject map[string]string `json:"activeProjects"`
}
func (f *Firebase) Template() string {
return " {{ .Project}} "
}
func (f *Firebase) Init(props properties.Properties, env platform.Environment) {
f.props = props
f.env = env
}
func (f *Firebase) Enabled() bool {
cfgDir := filepath.Join(f.env.Home(), ".config", "configstore")
configFile, err := f.getActiveConfig(cfgDir)
if err != nil {
f.env.Error(err)
return false
}
data, err := f.getFirebaseData(configFile)
if err != nil {
f.env.Error(err)
return false
}
// Within the activeProjects is a key value pair
// of the path to the project and the project name
// Test if the current directory is a project path
// and if it is, return the project name
for key, value := range data.ActiveProject {
if strings.HasPrefix(f.env.Pwd(), key) {
f.Project = value
return true
}
}
return false
}
func (f *Firebase) getActiveConfig(cfgDir string) (string, error) {
activeConfigFile := filepath.Join(cfgDir, "firebase-tools.json")
activeConfigData := f.env.FileContent(activeConfigFile)
if len(activeConfigData) == 0 {
return "", errors.New(FIREBASENOACTIVECONFIG)
}
return activeConfigData, nil
}
func (f *Firebase) getFirebaseData(configFile string) (*FirebaseData, error) {
var data FirebaseData
err := json.Unmarshal([]byte(configFile), &data)
if err != nil {
return nil, err
}
return &data, nil
}

View file

@ -0,0 +1,116 @@
package segments
import (
"path/filepath"
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/mock"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)
func TestFirebaseSegment(t *testing.T) {
config := `{
"activeProjects": {
"path": "project-name"
}
}`
cases := []struct {
Case string
ActiveConfig string
ActivePath string
ExpectedEnabled bool
ExpectedString string
}{
{
Case: "happy path",
ExpectedEnabled: true,
ActiveConfig: config,
ActivePath: "path",
ExpectedString: "project-name",
},
{
Case: "happy subpath",
ExpectedEnabled: true,
ActiveConfig: config,
ActivePath: "path/subpath",
ExpectedString: "project-name",
},
{
Case: "no active config",
ExpectedEnabled: false,
},
{
Case: "empty config",
ActiveConfig: "{}",
ExpectedEnabled: false,
},
{
Case: "bad config",
ActiveConfig: "{bad}",
ExpectedEnabled: false,
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("Home").Return("home")
env.On("Pwd").Return(tc.ActivePath)
fcPath := filepath.Join("home", ".config", "configstore", "firebase-tools.json")
env.On("FileContent", fcPath).Return(tc.ActiveConfig)
env.On("Error", mock2.Anything).Return()
f := Firebase{
env: env,
}
f.Enabled()
assert.Equal(t, tc.ExpectedEnabled, f.Enabled())
if tc.ExpectedEnabled {
assert.Equal(t, tc.ExpectedString, renderTemplate(env, f.Template(), f), tc.Case)
}
}
}
func TestGetFirebaseActiveConfig(t *testing.T) {
data :=
`{
"activeProjects": {
"path": "project-name"
}
}`
cases := []struct {
Case string
ActiveConfig string
ExpectedString string
ExpectedError string
}{
{
Case: "happy path",
ActiveConfig: data,
ExpectedString: data,
},
{
Case: "no active config",
ActiveConfig: "",
ExpectedError: FIREBASENOACTIVECONFIG,
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("Home").Return("home")
configPath := filepath.Join("home", ".config", "configstore")
contentPath := filepath.Join(configPath, "firebase-tools.json")
env.On("FileContent", contentPath).Return(tc.ActiveConfig)
env.On("Error", mock2.Anything).Return()
f := Firebase{
env: env,
}
got, err := f.getActiveConfig(configPath)
assert.Equal(t, tc.ExpectedString, got, tc.Case)
if len(tc.ExpectedError) > 0 {
assert.EqualError(t, err, tc.ExpectedError, tc.Case)
} else {
assert.NoError(t, err, tc.Case)
}
}
}

View file

@ -22,7 +22,7 @@ func TestGolang(t *testing.T) {
}{
{Case: "Go 1.15", ExpectedString: "1.15.8", Version: "go version go1.15.8 darwin/amd64"},
{Case: "Go 1.16", ExpectedString: "1.16", Version: "go version go1.16 darwin/amd64"},
{Case: "go.mod 1.21", ParseModFile: true, HasModFileInParentDir: true, ExpectedString: "1.21"},
{Case: "go.mod 1.22.3", ParseModFile: true, HasModFileInParentDir: true, ExpectedString: "1.22.3"},
{Case: "no go.mod file fallback", ParseModFile: true, ExpectedString: "1.16", Version: "go version go1.16 darwin/amd64"},
{
Case: "invalid go.mod file fallback",

View file

@ -19,7 +19,7 @@ func (h *Helm) Enabled() bool {
}
inChart := false
files := []string{"Chart.yml", "Chart.yaml"}
files := []string{"Chart.yml", "Chart.yaml", "helmfile.yaml", "helmfile.yml"}
for _, file := range files {
if _, err := h.env.HasParentFilePath(file); err == nil {
inChart = true

View file

@ -57,6 +57,22 @@ func TestHelmSegment(t *testing.T) {
DisplayMode: "files",
ChartFile: "Chart.yaml",
},
{
Case: "DisplayMode always inside chart. Chart file helmfile.yaml",
HelmExists: true,
ExpectedEnabled: true,
ExpectedString: "Helm 3.12.3",
DisplayMode: "files",
ChartFile: "helmfile.yaml",
},
{
Case: "DisplayMode always inside chart. Chart file helmfile.yml",
HelmExists: true,
ExpectedEnabled: true,
ExpectedString: "Helm 3.12.3",
DisplayMode: "files",
ChartFile: "helmfile.yml",
},
{
Case: "DisplayMode always outside chart",
HelmExists: true,

View file

@ -1,61 +0,0 @@
package segments
import (
"errors"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
)
type ITerm struct {
props properties.Properties
env platform.Environment
PromptMark string
}
func (i *ITerm) Template() string {
return "{{ .PromptMark }}"
}
func (i *ITerm) Enabled() bool {
promptMark, err := i.getResult()
if err != nil {
i.env.Error(err)
return false
}
i.PromptMark = promptMark
return true
}
func (i *ITerm) getResult() (string, error) {
var response string
// First, check if we're using iTerm
if i.env.Getenv("TERM_PROGRAM") != "iTerm.app" {
return "", errors.New("Only works with iTerm")
}
// Check to ensure the user has squelched the default mark for BASH and ZSH
if i.env.Getenv("ITERM2_SQUELCH_MARK") != "1" {
return "", errors.New("iTerm default mark enabled (export ITERM2_SQUELCH_MARK=1)")
}
// Now, set the mark string based on shell (or error out)
switch i.env.Shell() {
case shell.ZSH:
response = `%{$(iterm2_prompt_mark)%}`
case shell.BASH:
response = `\[$(iterm2_prompt_mark)\]`
default:
return "", errors.New("Shell isn't compatible with iTerm Shell Integration")
}
return response, nil
}
func (i *ITerm) Init(props properties.Properties, env platform.Environment) {
i.props = props
i.env = env
}

View file

@ -1,46 +0,0 @@
package segments
import (
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/mock"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)
func TestITermSegment(t *testing.T) {
cases := []struct {
Case string
TermProgram string
SquelchMark string
Shell string
Template string
ExpectedString string
ExpectedDisabled bool
}{
{Case: "not iterm", TermProgram: "", SquelchMark: "1", Shell: "zsh", ExpectedDisabled: true},
{Case: "default mark", TermProgram: "iTerm.app", Shell: "zsh", Template: "{{ .PromptMark }}", ExpectedDisabled: true},
{Case: "zsh", TermProgram: "iTerm.app", SquelchMark: "1", Shell: "zsh", Template: "{{ .PromptMark }}", ExpectedString: `%{$(iterm2_prompt_mark)%}`},
{Case: "bash", TermProgram: "iTerm.app", SquelchMark: "1", Shell: "bash", Template: "{{ .PromptMark }}", ExpectedString: `\[$(iterm2_prompt_mark)\]`},
{Case: "fish", TermProgram: "iTerm.app", SquelchMark: "1", Shell: "fish", Template: "{{ .PromptMark }}", ExpectedDisabled: true},
{Case: "pwsh", TermProgram: "iTerm.app", SquelchMark: "1", Shell: "pwsh", Template: "{{ .PromptMark }}", ExpectedDisabled: true},
{Case: "gibberishshell", TermProgram: "iTerm.app", SquelchMark: "1", Shell: "jaserhuashf", Template: "{{ .PromptMark }}", ExpectedDisabled: true},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("PathSeparator").Return("/")
env.On("Getenv", "TERM_PROGRAM").Return(tc.TermProgram)
env.On("Getenv", "ITERM2_SQUELCH_MARK").Return(tc.SquelchMark)
env.On("Shell").Return(tc.Shell)
env.On("Error", mock2.Anything).Return()
iterm := &ITerm{
env: env,
}
assert.Equal(t, !tc.ExpectedDisabled, iterm.Enabled(), tc.Case)
if !tc.ExpectedDisabled {
assert.Equal(t, tc.ExpectedString, renderTemplate(env, tc.Template, iterm), tc.Case)
}
}
}

View file

@ -3,6 +3,7 @@ package segments
import (
"encoding/json"
"errors"
http2 "net/http"
"time"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
@ -20,7 +21,8 @@ type Nightscout struct {
const (
// Your complete Nightscout URL and APIKey like this
URL properties.Property = "url"
URL properties.Property = "url"
Headers properties.Property = "headers"
DoubleUpIcon properties.Property = "doubleup_icon"
SingleUpIcon properties.Property = "singleup_icon"
@ -116,7 +118,14 @@ func (ns *Nightscout) getResult() (*NightscoutData, error) {
}
}
body, err := ns.env.HTTPRequest(url, nil, httpTimeout)
headers := ns.props.GetKeyValueMap(Headers, map[string]string{})
modifiers := func(request *http2.Request) {
for key, value := range headers {
request.Header.Add(key, value)
}
}
body, err := ns.env.HTTPRequest(url, nil, httpTimeout, modifiers)
if err != nil {
return nil, err
}

View file

@ -136,6 +136,7 @@ func TestNSSegment(t *testing.T) {
props := properties.Map{
properties.CacheTimeout: tc.CacheTimeout,
URL: "FAKE",
Headers: map[string]string{"Fake-Header": "xxxxx"},
}
cache := &mock.MockedCache{}

View file

@ -32,7 +32,7 @@ func (n *Node) Init(props properties.Properties, env platform.Environment) {
n.language = language{
env: env,
props: props,
extensions: []string{"*.js", "*.ts", "package.json", ".nvmrc", "pnpm-workspace.yaml", ".pnpmfile.cjs", ".npmrc", ".vue"},
extensions: []string{"*.js", "*.ts", "package.json", ".nvmrc", "pnpm-workspace.yaml", ".pnpmfile.cjs", ".vue"},
commands: []*cmd{
{
executable: "node",

View file

@ -54,13 +54,14 @@ func (oi *Os) getDistroIcon(distro string) string {
"almalinux": "\uF31D",
"almalinux9": "\uF31D",
"alpine": "\uF300",
"android": "\uF17b",
"aosc": "\uF301",
"arch": "\uF303",
"centos": "\uF304",
"coreos": "\uF305",
"debian": "\uF306",
"deepin": "\uF321",
"devuan": "\uF307",
"raspbian": "\uF315",
"elementary": "\uF309",
"fedora": "\uF30a",
"gentoo": "\uF30d",
@ -70,12 +71,12 @@ func (oi *Os) getDistroIcon(distro string) string {
"nixos": "\uF313",
"opensuse": "\uF314",
"opensuse-tumbleweed": "\uF314",
"raspbian": "\uF315",
"redhat": "\uF316",
"rocky": "\uF32B",
"sabayon": "\uF317",
"slackware": "\uF319",
"ubuntu": "\uF31b",
"android": "\uf17b",
}
if icon, ok := iconMap[distro]; ok {

View file

@ -86,7 +86,7 @@ func TestOWMSegmentSingle(t *testing.T) {
for _, tc := range cases {
env := &mock.MockedEnvironment{}
var props properties.Map
if tc.Latitude != 0 && tc.Longitude != 0 && tc.Location != "" {
if tc.Latitude != 0 && tc.Longitude != 0 && tc.Location != "" { //nolint: gocritic
props = properties.Map{
APIKey: "key",
Location: tc.Location,

View file

@ -25,6 +25,7 @@ type Path struct {
Location string
Writable bool
RootDir bool
Folders Folders
}
const (
@ -48,8 +49,8 @@ const (
Short string = "short"
// Full displays the full path
Full string = "full"
// Folder displays the current folder
Folder string = "folder"
// FolderType displays the current folder
FolderType string = "folder"
// Mixed like agnoster, but if a folder name is short enough, it is displayed as-is
Mixed string = "mixed"
// Letter like agnoster, but with the first letter of each folder name
@ -81,6 +82,12 @@ const (
FolderFormat properties.Property = "folder_format"
// format to use on the first and last folder of the path
EdgeFormat properties.Property = "edge_format"
// format to use on first folder of the path
LeftFormat properties.Property = "left_format"
// format to use on the last folder of the path
RightFormat properties.Property = "right_format"
// GitDirFormat format to use on the git directory
GitDirFormat properties.Property = "gitdir_format"
)
func (pt *Path) Template() string {
@ -105,25 +112,34 @@ func (pt *Path) Enabled() bool {
}
func (pt *Path) setPaths() {
defer func() {
pt.Folders = pt.splitPath()
}()
pt.pwd = pt.env.Pwd()
if (pt.env.Shell() == shell.PWSH || pt.env.Shell() == shell.PWSH5) && len(pt.env.Flags().PSWD) != 0 {
pt.pwd = pt.env.Flags().PSWD
}
if len(pt.pwd) == 0 {
return
}
// ensure a clean path
pt.root, pt.relative = pt.replaceMappedLocations()
// this is a full replacement of the parent
if len(pt.root) == 0 {
pt.pwd = pt.relative
return
}
pathSeparator := pt.env.PathSeparator()
if !strings.HasSuffix(pt.root, pathSeparator) && len(pt.relative) > 0 {
pt.pwd = pt.root + pathSeparator + pt.relative
return
}
pt.pwd = pt.root + pt.relative
}
@ -173,7 +189,7 @@ func (pt *Path) setStyle() {
fallthrough
case Full:
pt.Path = pt.getFullPath()
case Folder:
case FolderType:
pt.Path = pt.getFolderPath()
case Powerlevel:
maxWidth := int(pt.props.GetFloat64(MaxWidth, 0))
@ -213,15 +229,17 @@ func (pt *Path) getMixedPath() string {
threshold := int(pt.props.GetFloat64(MixedThreshold, 4))
folderIcon := pt.props.GetString(FolderIcon, "..")
separator := pt.getFolderSeparator()
elements := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root != pt.env.PathSeparator() {
elements = append([]string{pt.root}, elements...)
pt.Folders = append(Folders{{Name: pt.root}}, pt.Folders...)
}
n := len(elements)
buffer.WriteString(elements[0])
n := len(pt.Folders)
buffer.WriteString(pt.Folders[0].Name)
for i := 1; i < n; i++ {
folder := elements[i]
if len(folder) > threshold && i != n-1 {
folder := pt.Folders[i].Name
if len(folder) > threshold && i != n-1 && !pt.Folders[i].Display {
folder = folderIcon
}
buffer.WriteString(fmt.Sprintf("%s%s", separator, folder))
@ -229,33 +247,28 @@ func (pt *Path) getMixedPath() string {
return buffer.String()
}
func (pt *Path) pathDepth(pwd string) int {
splitted := strings.Split(pwd, pt.env.PathSeparator())
depth := 0
for _, part := range splitted {
if part != "" {
depth++
}
}
return depth
}
func (pt *Path) getAgnosterPath() string {
folderIcon := pt.props.GetString(FolderIcon, "..")
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
pt.root = pt.Folders[0].Name
pt.Folders = pt.Folders[1:]
}
var elements []string
n := len(splitted)
for i := 1; i < n; i++ {
elements = append(elements, folderIcon)
n := len(pt.Folders)
for i := 0; i < n-1; i++ {
name := folderIcon
if pt.Folders[i].Display {
name = pt.Folders[i].Name
}
elements = append(elements, name)
}
if len(splitted) > 0 {
elements = append(elements, splitted[n-1])
if len(pt.Folders) > 0 {
elements = append(elements, pt.Folders[n-1].Name)
}
return pt.colorizePath(pt.root, elements)
@ -263,52 +276,66 @@ func (pt *Path) getAgnosterPath() string {
func (pt *Path) getAgnosterLeftPath() string {
folderIcon := pt.props.GetString(FolderIcon, "..")
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
pt.root = pt.Folders[0].Name
pt.Folders = pt.Folders[1:]
}
var elements []string
n := len(splitted)
elements = append(elements, splitted[0])
n := len(pt.Folders)
elements = append(elements, pt.Folders[0].Name)
for i := 1; i < n; i++ {
if pt.Folders[i].Display {
elements = append(elements, pt.Folders[i].Name)
continue
}
elements = append(elements, folderIcon)
}
return pt.colorizePath(pt.root, elements)
}
func (pt *Path) getRelevantLetter(folder string) string {
func (pt *Path) getRelevantLetter(folder *Folder) string {
if folder.Display {
return folder.Name
}
// check if there is at least a letter we can use
matches := regex.FindNamedRegexMatch(`(?P<letter>[\p{L}0-9]).*`, folder)
matches := regex.FindNamedRegexMatch(`(?P<letter>[\p{L}0-9]).*`, folder.Name)
if matches == nil || len(matches["letter"]) == 0 {
// no letter found, keep the folder unchanged
return folder
return folder.Name
}
letter := matches["letter"]
// handle non-letter characters before the first found letter
letter = folder[0:strings.Index(folder, letter)] + letter
letter = folder.Name[0:strings.Index(folder.Name, letter)] + letter
return letter
}
func (pt *Path) getLetterPath() string {
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
pt.root = pt.Folders[0].Name
pt.Folders = pt.Folders[1:]
}
pt.root = pt.getRelevantLetter(pt.root)
pt.root = pt.getRelevantLetter(&Folder{Name: pt.root})
var elements []string
n := len(splitted)
n := len(pt.Folders)
for i := 0; i < n-1; i++ {
letter := pt.getRelevantLetter(splitted[i])
if pt.Folders[i].Display {
elements = append(elements, pt.Folders[i].Name)
continue
}
letter := pt.getRelevantLetter(pt.Folders[i])
elements = append(elements, letter)
}
if len(splitted) > 0 {
elements = append(elements, splitted[n-1])
if len(pt.Folders) > 0 {
elements = append(elements, pt.Folders[n-1].Name)
}
return pt.colorizePath(pt.root, elements)
@ -316,98 +343,101 @@ func (pt *Path) getLetterPath() string {
func (pt *Path) getUniqueLettersPath(maxWidth int) string {
separator := pt.getFolderSeparator()
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
pt.root = pt.Folders[0].Name
pt.Folders = pt.Folders[1:]
}
if maxWidth > 0 {
path := strings.Join(splitted, separator)
path := strings.Join(pt.Folders.List(), separator)
if len(path) <= maxWidth {
return pt.colorizePath(pt.root, splitted)
return pt.colorizePath(pt.root, pt.Folders.List())
}
}
pt.root = pt.getRelevantLetter(pt.root)
pt.root = pt.getRelevantLetter(&Folder{Name: pt.root})
var elements []string
n := len(splitted)
n := len(pt.Folders)
letters := make(map[string]bool)
letters[pt.root] = true
for i := 0; i < n-1; i++ {
folder := splitted[i]
letter := pt.getRelevantLetter(folder)
folder := pt.Folders[i].Name
letter := pt.getRelevantLetter(pt.Folders[i])
for letters[letter] {
if letter == folder {
break
}
letter += folder[len(letter) : len(letter)+1]
}
letters[letter] = true
elements = append(elements, letter)
// only return early on maxWidth > 0
// this enables the powerlevel10k behavior
if maxWidth > 0 {
list := splitted[i+1:]
list := pt.Folders[i+1:].List()
list = append(list, elements...)
current := strings.Join(list, separator)
leftover := maxWidth - len(current) - len(pt.root) - len(separator)
if leftover >= 0 {
elements = append(elements, strings.Join(splitted[i+1:], separator))
elements = append(elements, strings.Join(pt.Folders[i+1:].List(), separator))
return pt.colorizePath(pt.root, elements)
}
}
}
if len(splitted) > 0 {
elements = append(elements, splitted[n-1])
if len(pt.Folders) > 0 {
elements = append(elements, pt.Folders[n-1].Name)
}
return pt.colorizePath(pt.root, elements)
}
func (pt *Path) getAgnosterFullPath() string {
splitted := strings.Split(pt.relative, pt.env.PathSeparator())
if pt.root == pt.env.PathSeparator() {
pt.root = splitted[0]
splitted = splitted[1:]
pt.root = pt.Folders[0].Name
pt.Folders = pt.Folders[1:]
}
return pt.colorizePath(pt.root, splitted)
return pt.colorizePath(pt.root, pt.Folders.List())
}
func (pt *Path) getAgnosterShortPath() string {
pathDepth := pt.pathDepth(pt.relative)
pathDepth := len(pt.Folders)
maxDepth := pt.props.GetInt(MaxDepth, 1)
if maxDepth < 1 {
maxDepth = 1
}
folderIcon := pt.props.GetString(FolderIcon, "..")
hideRootLocation := pt.props.GetBool(HideRootLocation, false)
if pathDepth <= maxDepth {
if hideRootLocation {
pt.root = folderIcon
}
return pt.getAgnosterFullPath()
}
pathSeparator := pt.env.PathSeparator()
rel := strings.TrimPrefix(pt.relative, pathSeparator)
splitted := strings.Split(rel, pathSeparator)
splitPos := pathDepth - maxDepth
// var buffer strings.Builder
var elements []string
var folders []string
// unix root, needs to be replaced with the folder we're in at root level
root := pt.root
room := pathDepth - maxDepth
if root == pathSeparator {
root = splitted[0]
root = pt.Folders[0].Name
room--
}
if hideRootLocation || room > 0 {
elements = append(elements, folderIcon)
folders = append(folders, folderIcon)
}
if hideRootLocation {
@ -415,14 +445,14 @@ func (pt *Path) getAgnosterShortPath() string {
}
for i := splitPos; i < pathDepth; i++ {
elements = append(elements, splitted[i])
folders = append(folders, pt.Folders[i].Name)
}
return pt.colorizePath(root, elements)
return pt.colorizePath(root, folders)
}
func (pt *Path) getFullPath() string {
elements := strings.Split(pt.relative, pt.env.PathSeparator())
return pt.colorizePath(pt.root, elements)
return pt.colorizePath(pt.root, pt.Folders.List())
}
func (pt *Path) getFolderPath() string {
@ -597,7 +627,10 @@ func (pt *Path) colorizePath(root string, elements []string) string {
folderSeparator := pt.getFolderSeparator()
colorSeparator := pt.props.GetBool(CycleFolderSeparator, false)
folderFormat := pt.props.GetString(FolderFormat, "%s")
edgeFormat := pt.props.GetString(EdgeFormat, folderFormat)
leftFormat := pt.props.GetString(LeftFormat, edgeFormat)
rightFormat := pt.props.GetString(RightFormat, edgeFormat)
colorizeElement := func(element string) string {
if skipColorize || len(element) == 0 {
@ -610,7 +643,7 @@ func (pt *Path) colorizePath(root string, elements []string) string {
}
if len(elements) == 0 {
root := fmt.Sprintf(edgeFormat, root)
root := fmt.Sprintf(leftFormat, root)
return colorizeElement(root)
}
@ -623,7 +656,7 @@ func (pt *Path) colorizePath(root string, elements []string) string {
var builder strings.Builder
root = fmt.Sprintf(edgeFormat, root)
root = fmt.Sprintf(leftFormat, root)
builder.WriteString(colorizeElement(root))
if root != pt.env.PathSeparator() && len(root) != 0 {
@ -637,7 +670,7 @@ func (pt *Path) colorizePath(root string, elements []string) string {
format := folderFormat
if i == len(elements)-1 {
format = edgeFormat
format = rightFormat
}
element = fmt.Sprintf(format, element)
@ -649,3 +682,78 @@ func (pt *Path) colorizePath(root string, elements []string) string {
return builder.String()
}
type Folder struct {
Name string
Display bool
Path string
}
type Folders []*Folder
func (f Folders) List() []string {
var list []string
for _, folder := range f {
list = append(list, folder.Name)
}
return list
}
func (pt *Path) splitPath() Folders {
result := Folders{}
folders := []string{}
if len(pt.relative) != 0 {
folders = strings.Split(pt.relative, pt.env.PathSeparator())
}
folderFormatMap := pt.makeFolderFormatMap()
getCurrentPath := func() string {
if pt.root == "~" {
return pt.env.Home() + pt.env.PathSeparator()
}
if pt.env.GOOS() == platform.WINDOWS {
return pt.root + pt.env.PathSeparator()
}
return pt.root
}
currentPath := getCurrentPath()
var display bool
for _, folder := range folders {
currentPath += folder
if format := folderFormatMap[currentPath]; len(format) != 0 {
folder = fmt.Sprintf(format, folder)
display = true
}
result = append(result, &Folder{Name: folder, Path: currentPath, Display: display})
currentPath += pt.env.PathSeparator()
display = false
}
return result
}
func (pt *Path) makeFolderFormatMap() map[string]string {
folderFormatMap := make(map[string]string)
if gitDirFormat := pt.props.GetString(GitDirFormat, ""); len(gitDirFormat) != 0 {
dir, err := pt.env.HasParentFilePath(".git")
if err == nil && dir.IsDir {
folderFormatMap[dir.ParentFolder] = gitDirFormat
}
}
return folderFormatMap
}

View file

@ -787,21 +787,21 @@ func TestFullAndFolderPath(t *testing.T) {
{Style: Full, FolderSeparatorIcon: "|", Pwd: homeDir + abc, Expected: "~|abc"},
{Style: Full, FolderSeparatorIcon: "|", Pwd: abcd, Expected: "/a|b|c|d"},
{Style: Folder, Pwd: "/", Expected: "/"},
{Style: Folder, Pwd: homeDir, Expected: "~"},
{Style: Folder, Pwd: homeDir, Expected: "someone", DisableMappedLocations: true},
{Style: Folder, Pwd: homeDir + abc, Expected: "abc"},
{Style: Folder, Pwd: abcd, Expected: "d"},
{Style: FolderType, Pwd: "/", Expected: "/"},
{Style: FolderType, Pwd: homeDir, Expected: "~"},
{Style: FolderType, Pwd: homeDir, Expected: "someone", DisableMappedLocations: true},
{Style: FolderType, Pwd: homeDir + abc, Expected: "abc"},
{Style: FolderType, Pwd: abcd, Expected: "d"},
{Style: Folder, FolderSeparatorIcon: "|", Pwd: "/", Expected: "/"},
{Style: Folder, FolderSeparatorIcon: "|", Pwd: homeDir, Expected: "~"},
{Style: Folder, FolderSeparatorIcon: "|", Pwd: homeDir, Expected: "someone", DisableMappedLocations: true},
{Style: Folder, FolderSeparatorIcon: "|", Pwd: homeDir + abc, Expected: "abc"},
{Style: Folder, FolderSeparatorIcon: "|", Pwd: abcd, Expected: "d"},
{Style: FolderType, FolderSeparatorIcon: "|", Pwd: "/", Expected: "/"},
{Style: FolderType, FolderSeparatorIcon: "|", Pwd: homeDir, Expected: "~"},
{Style: FolderType, FolderSeparatorIcon: "|", Pwd: homeDir, Expected: "someone", DisableMappedLocations: true},
{Style: FolderType, FolderSeparatorIcon: "|", Pwd: homeDir + abc, Expected: "abc"},
{Style: FolderType, FolderSeparatorIcon: "|", Pwd: abcd, Expected: "d"},
// for Windows paths
{Style: Folder, FolderSeparatorIcon: "\\", Pwd: "C:\\", Expected: "C:\\", PathSeparator: "\\", GOOS: platform.WINDOWS},
{Style: Folder, FolderSeparatorIcon: "\\", Pwd: homeDirWindows, Expected: "~", PathSeparator: "\\", GOOS: platform.WINDOWS},
{Style: FolderType, FolderSeparatorIcon: "\\", Pwd: "C:\\", Expected: "C:\\", PathSeparator: "\\", GOOS: platform.WINDOWS},
{Style: FolderType, FolderSeparatorIcon: "\\", Pwd: homeDirWindows, Expected: "~", PathSeparator: "\\", GOOS: platform.WINDOWS},
{Style: Full, FolderSeparatorIcon: "\\", Pwd: homeDirWindows, Expected: "~", PathSeparator: "\\", GOOS: platform.WINDOWS},
{Style: Full, FolderSeparatorIcon: "\\", Pwd: homeDirWindows + "\\abc", Expected: "~\\abc", PathSeparator: "\\", GOOS: platform.WINDOWS},
{Style: Full, FolderSeparatorIcon: "\\", Pwd: "C:\\Users\\posh", Expected: "C:\\Users\\posh", PathSeparator: "\\", GOOS: platform.WINDOWS},
@ -994,7 +994,7 @@ func TestFolderPathCustomMappedLocations(t *testing.T) {
path := &Path{
env: env,
props: properties.Map{
properties.Style: Folder,
properties.Style: FolderType,
MappedLocations: map[string]string{
abcd: "#",
},
@ -1502,3 +1502,71 @@ func TestReplaceMappedLocations(t *testing.T) {
assert.Equal(t, tc.Expected, path.pwd)
}
}
func TestSplitPath(t *testing.T) {
cases := []struct {
Case string
GOOS string
Relative string
Root string
GitDir *platform.FileInfo
GitDirFormat string
Expected Folders
}{
{Case: "Root directory", Root: "/", Expected: Folders{}},
{
Case: "Regular directory",
Root: "/",
Relative: "c/d",
GOOS: platform.DARWIN,
Expected: Folders{
{Name: "c", Path: "/c"},
{Name: "d", Path: "/c/d"},
},
},
{
Case: "Home directory - git folder",
Root: "~",
Relative: "c/d",
GOOS: platform.DARWIN,
GitDir: &platform.FileInfo{IsDir: true, ParentFolder: "/a/b/c"},
GitDirFormat: "<b>%s</b>",
Expected: Folders{
{Name: "<b>c</b>", Path: "/a/b/c", Display: true},
{Name: "d", Path: "/a/b/c/d"},
},
},
{
Case: "Home directory - git folder on Windows",
Root: "C:",
Relative: "a/b/c/d",
GOOS: platform.WINDOWS,
GitDir: &platform.FileInfo{IsDir: true, ParentFolder: "C:/a/b/c"},
GitDirFormat: "<b>%s</b>",
Expected: Folders{
{Name: "a", Path: "C:/a"},
{Name: "b", Path: "C:/a/b"},
{Name: "<b>c</b>", Path: "C:/a/b/c", Display: true},
{Name: "d", Path: "C:/a/b/c/d"},
},
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("PathSeparator").Return("/")
env.On("Home").Return("/a/b")
env.On("HasParentFilePath", ".git").Return(tc.GitDir, nil)
env.On("GOOS").Return(tc.GOOS)
path := &Path{
env: env,
props: properties.Map{
GitDirFormat: tc.GitDirFormat,
},
root: tc.Root,
relative: tc.Relative,
}
got := path.splitPath()
assert.Equal(t, tc.Expected, got, tc.Case)
}
}

View file

@ -12,7 +12,7 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/regex"
"golang.org/x/exp/slices"
"github.com/BurntSushi/toml"
toml "github.com/pelletier/go-toml/v2"
)
type ProjectItem struct {
@ -154,7 +154,7 @@ func (n *Project) getCargoPackage(item ProjectItem) *ProjectData {
content := n.env.FileContent(item.Files[0])
var data CargoTOML
_, err := toml.Decode(content, &data)
err := toml.Unmarshal([]byte(content), &data)
if err != nil {
n.Error = err.Error()
return nil
@ -170,7 +170,7 @@ func (n *Project) getPythonPackage(item ProjectItem) *ProjectData {
content := n.env.FileContent(item.Files[0])
var data PyProjectTOML
_, err := toml.Decode(content, &data)
err := toml.Unmarshal([]byte(content), &data)
if err != nil {
n.Error = err.Error()
return nil
@ -293,7 +293,7 @@ func (n *Project) getProjectData(item ProjectItem) *ProjectData {
content := n.env.FileContent(item.Files[0])
var data ProjectData
_, err := toml.Decode(content, &data)
err := toml.Unmarshal([]byte(content), &data)
if err != nil {
n.Error = err.Error()
return nil

220
src/segments/pulumi.go Normal file
View file

@ -0,0 +1,220 @@
package segments
import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"path/filepath"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"gopkg.in/yaml.v3"
)
const (
FetchStack properties.Property = "fetch_stack"
FetchAbout properties.Property = "fetch_about"
JSON string = "json"
YAML string = "yaml"
pulumiJSON string = "Pulumi.json"
pulumiYAML string = "Pulumi.yaml"
)
type Pulumi struct {
props properties.Properties
env platform.Environment
Stack string
Name string
workspaceSHA1 string
backend
}
type backend struct {
URL string `json:"url"`
User string `json:"user"`
}
type pulumiFileSpec struct {
Name string `yaml:"name" json:"name"`
}
type pulumiWorkSpaceFileSpec struct {
Stack string `json:"stack"`
}
func (p *Pulumi) Template() string {
return "\U000f0d46 {{ .Stack }}{{if .User }} :: {{ .User }}@{{ end }}{{ if .URL }}{{ .URL }}{{ end }}"
}
func (p *Pulumi) Init(props properties.Properties, env platform.Environment) {
p.props = props
p.env = env
}
func (p *Pulumi) Enabled() bool {
if !p.env.HasCommand("pulumi") {
return false
}
err := p.getProjectName()
if err != nil {
p.env.Error(err)
return false
}
if p.props.GetBool(FetchStack, false) {
p.getPulumiStackName()
}
if p.props.GetBool(FetchAbout, false) {
p.getPulumiAbout()
}
return true
}
func (p *Pulumi) getPulumiStackName() {
if len(p.Name) == 0 || len(p.workspaceSHA1) == 0 {
p.env.Debug("pulumi project name or workspace sha1 is empty")
return
}
stackNameFile := p.Name + "-" + p.workspaceSHA1 + "-" + "workspace.json"
homedir := p.env.Home()
workspaceCacheDir := filepath.Join(homedir, ".pulumi", "workspaces")
if !p.env.HasFolder(workspaceCacheDir) || !p.env.HasFilesInDir(workspaceCacheDir, stackNameFile) {
return
}
workspaceCacheFile := filepath.Join(workspaceCacheDir, stackNameFile)
workspaceCacheFileContent := p.env.FileContent(workspaceCacheFile)
var pulumiWorkspaceSpec pulumiWorkSpaceFileSpec
err := json.Unmarshal([]byte(workspaceCacheFileContent), &pulumiWorkspaceSpec)
if err != nil {
p.env.Error(fmt.Errorf("pulumi workspace file decode error"))
return
}
p.env.DebugF("pulumi stack name: %s", pulumiWorkspaceSpec.Stack)
p.Stack = pulumiWorkspaceSpec.Stack
}
func (p *Pulumi) getProjectName() error {
var kind, fileName string
for _, file := range []string{pulumiYAML, pulumiJSON} {
if p.env.HasFiles(file) {
fileName = file
kind = filepath.Ext(file)[1:]
}
}
if len(kind) == 0 {
return fmt.Errorf("no pulumi spec file found")
}
var pulumiFileSpec pulumiFileSpec
var err error
pulumiFile := p.env.FileContent(fileName)
switch kind {
case YAML:
err = yaml.Unmarshal([]byte(pulumiFile), &pulumiFileSpec)
case JSON:
err = json.Unmarshal([]byte(pulumiFile), &pulumiFileSpec)
default:
err = fmt.Errorf("unknown pulumi spec file format")
}
if err != nil {
p.env.Error(err)
return nil
}
p.Name = pulumiFileSpec.Name
sha1HexString := func(value string) string {
h := sha1.New()
_, err := h.Write([]byte(value))
if err != nil {
p.env.Error(err)
return ""
}
return hex.EncodeToString(h.Sum(nil))
}
p.workspaceSHA1 = sha1HexString(p.env.Pwd() + p.env.PathSeparator() + fileName)
return nil
}
func (p *Pulumi) getPulumiAbout() {
if len(p.Stack) == 0 {
p.env.Error(fmt.Errorf("pulumi stack name is empty, use `fetch_stack` property to enable stack fetching"))
return
}
cacheKey := "pulumi-" + p.Name + "-" + p.Stack + "-" + p.workspaceSHA1 + "-about"
getAboutCache := func(key string) (*backend, error) {
aboutBackend, OK := p.env.Cache().Get(key)
if (!OK || len(aboutBackend) == 0) || (OK && len(aboutBackend) == 0) {
return nil, fmt.Errorf("no data in cache")
}
var backend *backend
err := json.Unmarshal([]byte(aboutBackend), &backend)
if err != nil {
p.env.DebugF("unable to decode about cache: %s", aboutBackend)
p.env.Error(fmt.Errorf("pulling about cache decode error"))
return nil, err
}
return backend, nil
}
aboutBackend, err := getAboutCache(cacheKey)
if err == nil {
p.backend = *aboutBackend
return
}
aboutOutput, err := p.env.RunCommand("pulumi", "about", "--json")
if err != nil {
p.env.Error(fmt.Errorf("unable to get pulumi about output"))
return
}
var about struct {
Backend *backend `json:"backend"`
}
err = json.Unmarshal([]byte(aboutOutput), &about)
if err != nil {
p.env.Error(fmt.Errorf("pulumi about output decode error"))
return
}
if about.Backend == nil {
p.env.Debug("pulumi about backend is not set")
return
}
p.backend = *about.Backend
cacheTimeout := p.props.GetInt(properties.CacheTimeout, 43800)
jso, _ := json.Marshal(about.Backend)
p.env.Cache().Set(cacheKey, string(jso), cacheTimeout)
}

236
src/segments/pulumi_test.go Normal file
View file

@ -0,0 +1,236 @@
package segments
import (
"errors"
"path/filepath"
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/mock"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)
func TestPulumi(t *testing.T) {
cases := []struct {
Case string
YAMLConfig string
JSONConfig string
HasCommand bool
FetchStack bool
Stack string
StackError error
HasWorkspaceFolder bool
WorkSpaceFile string
FetchAbout bool
About string
AboutError error
AboutCache string
ExpectedString string
ExpectedEnabled bool
}{
{
Case: "no pulumi command",
ExpectedEnabled: false,
HasCommand: false,
},
{
Case: "pulumi command is present, but no pulumi file",
ExpectedEnabled: false,
HasCommand: true,
},
{
Case: "pulumi file YAML is present",
ExpectedString: "\U000f0d46",
ExpectedEnabled: true,
HasCommand: true,
YAMLConfig: `
name: oh-my-posh
runtime: golang
description: A Console App
`,
},
{
Case: "pulumi file JSON is present",
ExpectedString: "\U000f0d46",
ExpectedEnabled: true,
HasCommand: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
},
{
Case: "no stack present",
ExpectedString: "\U000f0d46 1337",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
},
{
Case: "pulumi stack",
ExpectedString: "\U000f0d46 1337",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
},
{
Case: "pulumi URL",
ExpectedString: "\U000f0d46 1337 :: posh-user@s3://test-pulumi-state-test",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
FetchAbout: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
About: `{ "backend": { "url": "s3://test-pulumi-state-test", "user":"posh-user" } }`,
},
{
Case: "pulumi URL - cache",
ExpectedString: "\U000f0d46 1337 :: posh-user@s3://test-pulumi-state-test",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
FetchAbout: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
AboutCache: `{ "url": "s3://test-pulumi-state-test", "user":"posh-user" }`,
},
// Error flows
{
Case: "pulumi file JSON error",
ExpectedString: "\U000f0d46",
ExpectedEnabled: true,
FetchStack: true,
HasCommand: true,
JSONConfig: `{`,
},
{
Case: "pulumi workspace file JSON error",
ExpectedString: "\U000f0d46",
ExpectedEnabled: true,
FetchStack: true,
HasCommand: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{`,
HasWorkspaceFolder: true,
},
{
Case: "pulumi URL, no fetch_stack set",
ExpectedString: "\U000f0d46",
ExpectedEnabled: true,
HasCommand: true,
FetchAbout: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
},
{
Case: "pulumi URL - cache error",
ExpectedString: "\U000f0d46 1337 :: posh-user@s3://test-pulumi-state-test-output",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
FetchAbout: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
AboutCache: `{`,
About: `{ "backend": { "url": "s3://test-pulumi-state-test-output", "user":"posh-user" } }`,
},
{
Case: "pulumi URL - about error",
ExpectedString: "\U000f0d46 1337",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
FetchAbout: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
AboutError: errors.New("error"),
},
{
Case: "pulumi URL - about decode error",
ExpectedString: "\U000f0d46 1337",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
FetchAbout: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
About: `{`,
},
{
Case: "pulumi URL - about backend is nil",
ExpectedString: "\U000f0d46 1337",
ExpectedEnabled: true,
HasCommand: true,
HasWorkspaceFolder: true,
FetchStack: true,
FetchAbout: true,
JSONConfig: `{ "name": "oh-my-posh" }`,
WorkSpaceFile: `{ "stack": "1337" }`,
About: `{}`,
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("HasCommand", "pulumi").Return(tc.HasCommand)
env.On("RunCommand", "pulumi", []string{"stack", "ls", "--json"}).Return(tc.Stack, tc.StackError)
env.On("RunCommand", "pulumi", []string{"about", "--json"}).Return(tc.About, tc.AboutError)
env.On("Pwd").Return("/home/foobar/Work/oh-my-posh/pulumi/projects/awesome-project")
env.On("Home").Return(filepath.Clean("/home/foobar"))
env.On("Error", mock2.Anything)
env.On("Debug", mock2.Anything)
env.On("DebugF", mock2.Anything, mock2.Anything)
env.On("HasFiles", pulumiYAML).Return(len(tc.YAMLConfig) > 0)
env.On("FileContent", pulumiYAML).Return(tc.YAMLConfig, nil)
env.On("HasFiles", pulumiJSON).Return(len(tc.JSONConfig) > 0)
env.On("FileContent", pulumiJSON).Return(tc.JSONConfig, nil)
env.On("PathSeparator").Return("/")
env.On("HasFolder", filepath.Clean("/home/foobar/.pulumi/workspaces")).Return(tc.HasWorkspaceFolder)
workspaceFile := "oh-my-posh-c62b7b6786c5c5a85896576e46a25d7c9f888e92-workspace.json"
env.On("HasFilesInDir", filepath.Clean("/home/foobar/.pulumi/workspaces"), workspaceFile).Return(len(tc.WorkSpaceFile) > 0)
env.On("FileContent", filepath.Clean("/home/foobar/.pulumi/workspaces/"+workspaceFile)).Return(tc.WorkSpaceFile, nil)
cache := &mock.MockedCache{}
cache.On("Get", "pulumi-oh-my-posh-1337-c62b7b6786c5c5a85896576e46a25d7c9f888e92-about").Return(tc.AboutCache, len(tc.AboutCache) > 0)
cache.On("Set", mock2.Anything, mock2.Anything, mock2.Anything)
env.On("Cache").Return(cache)
pulumi := &Pulumi{
env: env,
props: properties.Map{
FetchStack: tc.FetchStack,
FetchAbout: tc.FetchAbout,
},
}
assert.Equal(t, tc.ExpectedEnabled, pulumi.Enabled(), tc.Case)
if !tc.ExpectedEnabled {
continue
}
var got = renderTemplate(env, pulumi.Template(), pulumi)
assert.Equal(t, tc.ExpectedString, got, tc.Case)
}
}

View file

@ -21,6 +21,7 @@ const (
// FetchVirtualEnv fetches the virtual env
FetchVirtualEnv properties.Property = "fetch_virtual_env"
UsePythonVersionFile properties.Property = "use_python_version_file"
FolderNameFallback properties.Property = "folder_name_fallback"
)
func (p *Python) Template() string {
@ -50,6 +51,11 @@ func (p *Python) Init(props properties.Properties, env platform.Environment) {
args: []string{"--version"},
regex: `(?:Python (?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+))))`,
},
{
executable: "py",
args: []string{"--version"},
regex: `(?:Python (?P<version>((?P<major>[0-9]+).(?P<minor>[0-9]+).(?P<patch>[0-9]+))))`,
},
},
versionURLTemplate: "https://docs.python.org/release/{{ .Major }}.{{ .Minor }}.{{ .Patch }}/whatsnew/changelog.html#python-{{ .Major }}-{{ .Minor }}-{{ .Patch }}",
displayMode: props.GetString(DisplayMode, DisplayModeEnvironment),
@ -74,6 +80,7 @@ func (p *Python) loadContext() {
"CONDA_DEFAULT_ENV",
}
folderNameFallback := p.language.props.GetBool(FolderNameFallback, true)
defaultVenvNames := []string{
".venv",
"venv",
@ -87,8 +94,7 @@ func (p *Python) loadContext() {
}
name := platform.Base(p.language.env, venv)
if slices.Contains(defaultVenvNames, name) {
if folderNameFallback && slices.Contains(defaultVenvNames, name) {
venv = strings.TrimSuffix(venv, name)
name = platform.Base(p.language.env, venv)
}

View file

@ -150,14 +150,36 @@ func TestPythonPythonInContext(t *testing.T) {
func TestPythonVirtualEnvIgnoreDefaultVenvNames(t *testing.T) {
cases := []struct {
VirtualEnvName string
Expected string
FolderNameFallback bool
VirtualEnvName string
}{
{VirtualEnvName: "/path/to/folder/.venv"},
{VirtualEnvName: "/path/to/folder/venv"},
{
Expected: "folder",
FolderNameFallback: true,
VirtualEnvName: "/path/to/folder/.venv",
},
{
Expected: "folder",
FolderNameFallback: true,
VirtualEnvName: "/path/to/folder/venv",
},
{
Expected: ".venv",
FolderNameFallback: false,
VirtualEnvName: "/path/to/folder/.venv",
},
{
Expected: "venv",
FolderNameFallback: false,
VirtualEnvName: "/path/to/folder/venv",
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
params := &mockedLanguageParams{}
env, props := getMockedLanguageEnv(params)
env.On("GOOS").Return("")
env.On("PathSeparator").Return("/")
env.On("CommandPath", mock2.Anything).Return("")
@ -167,9 +189,12 @@ func TestPythonVirtualEnvIgnoreDefaultVenvNames(t *testing.T) {
env.On("Getenv", "CONDA_DEFAULT_ENV").Return("")
env.On("Getenv", "PYENV_VERSION").Return("")
env.On("HasParentFilePath", ".python-version").Return(&platform.FileInfo{}, errors.New("no match at root level"))
props[FolderNameFallback] = tc.FolderNameFallback
python := &Python{}
python.Init(properties.Map{}, env)
python.Init(props, env)
python.loadContext()
assert.Equal(t, "folder", python.Venv)
assert.Equal(t, tc.Expected, python.Venv)
}
}

View file

@ -3,24 +3,28 @@
package segments
func (s *Spotify) Enabled() bool {
var err error
// Check if running
running := s.runAppleScriptCommand("application \"Spotify\" is running")
if running == "false" || running == "" {
s.Status = stopped
return false
}
s.Status = s.runAppleScriptCommand("tell application \"Spotify\" to player state as string")
if err != nil {
if len(s.Status) == 0 {
s.Status = stopped
return false
}
if s.Status == stopped {
return false
}
s.Artist = s.runAppleScriptCommand("tell application \"Spotify\" to artist of current track as string")
s.Track = s.runAppleScriptCommand("tell application \"Spotify\" to name of current track as string")
s.resolveIcon()
return true
}

View file

@ -3,7 +3,6 @@
package segments
import (
"encoding/csv"
"strings"
)
@ -11,24 +10,40 @@ func (s *Spotify) Enabled() bool {
if !s.env.IsWsl() {
return false
}
tlist, err := s.env.RunCommand("tasklist.exe", "/V", "/FI", "Imagename eq Spotify.exe", "/FO", "CSV", "/NH")
if err != nil || strings.HasPrefix(tlist, "INFO") {
return false
}
records, err := csv.NewReader(strings.NewReader(tlist)).ReadAll()
if err != nil || len(records) == 0 {
records := strings.Split(tlist, "\n")
if len(records) == 0 {
return false
}
for _, record := range records {
title := record[len(record)-1]
if strings.Contains(title, " - ") {
infos := strings.Split(title, " - ")
s.Artist = infos[0]
s.Track = strings.Join(infos[1:], " - ")
s.Status = playing
s.resolveIcon()
return true
record = strings.TrimSpace(record)
fields := strings.Split(record, ",")
if len(fields) == 0 {
continue
}
// last elemant is the title
title := fields[len(fields)-1]
// trim leading and trailing quotes from the field
title = strings.TrimPrefix(title, `"`)
title = strings.TrimSuffix(title, `"`)
if !strings.Contains(title, " - ") {
continue
}
infos := strings.Split(title, " - ")
s.Artist = infos[0]
s.Track = strings.Join(infos[1:], " - ")
s.Status = playing
s.resolveIcon()
return true
}
return false
}

View file

@ -46,6 +46,17 @@ func TestSpotifyWsl(t *testing.T) {
"Spotify.exe","22052","Console","1","29,040 K","Unknown","PC\user","0:00:00","N/A"
"Spotify.exe","22072","Console","1","43,960 K","Unknown","PC\user","0:01:50","N/A"
"Spotify.exe","10404","Console","1","256,924 K","Unknown","PC\user","0:10:49","N/A"`,
},
{
Case: "Spotify playing",
ExpectedString: "\ue602 Grabbitz - Another Form Of \"Goodbye\"",
ExpectedEnabled: true,
ExecOutput: `"Spotify.exe","13748","Console","1","303.744 K","Running","GARMIN\elderbroekowe","0:03:58","Grabbitz - Another Form Of "Goodbye""
"Spotify.exe","4208","Console","1","31.544 K","Running","GARMIN\elderbroekowe","0:00:00","N/A"
"Spotify.exe","14528","Console","1","184.020 K","Running","GARMIN\elderbroekowe","0:02:54","N/A"
"Spotify.exe","14488","Console","1","53.828 K","Unknown","GARMIN\elderbroekowe","0:00:08","N/A"
"Spotify.exe","14800","Console","1","29.576 K","Unknown","GARMIN\elderbroekowe","0:00:00","N/A"
"Spotify.exe","19836","Console","1","237.360 K","Unknown","GARMIN\elderbroekowe","0:07:46","N/A"`,
},
{
Case: "tasklist.exe not in path",

View file

@ -1,6 +1,7 @@
package segments
import (
"path"
"strconv"
"strings"
@ -123,6 +124,18 @@ func (s *Svn) setSvnStatus() {
}
}
func (s *Svn) Repo() string {
// Get the repository name as the last path element of the repository root URL
repo := s.getSvnCommandOutput("info", "--show-item", "repos-root-url")
base := path.Base(repo)
if base == "." {
return ""
}
return base
}
func (s *Svn) getSvnCommandOutput(command string, args ...string) string {
args = append([]string{command, s.realDir}, args...)
val, err := s.env.RunCommand(s.command, args...)

View file

@ -262,3 +262,44 @@ R Moved.File`,
assert.Equal(t, tc.ExpectedConflicts, s.Working.HasConflicts(), tc.Case)
}
}
func TestRepo(t *testing.T) {
cases := []struct {
Case string
Repo string
Expected string
}{
{
Case: "No repo",
Repo: "",
Expected: "",
},
{
Case: "Repo with trailing slash",
Repo: "http://example.com/",
Expected: "example.com",
},
{
Case: "Repo without trailing slash",
Repo: "http://example.com",
Expected: "example.com",
},
{
Case: "Repo with a path",
Repo: "http://example.com/test/repo",
Expected: "repo",
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("RunCommand", "svn", []string{"info", "", "--show-item", "repos-root-url"}).Return(tc.Repo, nil)
s := &Svn{
scm: scm{
env: env,
command: SVNCOMMAND,
},
}
assert.Equal(t, tc.Expected, s.Repo(), tc.Case)
}
}

56
src/segments/talosctl.go Normal file
View file

@ -0,0 +1,56 @@
package segments
import (
"errors"
"path/filepath"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"gopkg.in/yaml.v3"
)
type TalosCTL struct {
props properties.Properties
env platform.Environment
Context string `yaml:"context"`
}
func (t *TalosCTL) Template() string {
return " {{ .Context}} "
}
func (t *TalosCTL) Init(props properties.Properties, env platform.Environment) {
t.props = props
t.env = env
}
func (t *TalosCTL) Enabled() bool {
cfgDir := filepath.Join(t.env.Home(), ".talos")
configFile, err := t.getActiveConfig(cfgDir)
if err != nil {
t.env.Error(err)
return false
}
err = yaml.Unmarshal([]byte(configFile), t)
if err != nil {
t.env.Error(err)
return false
}
if len(t.Context) == 0 {
return false
}
return true
}
func (t *TalosCTL) getActiveConfig(cfgDir string) (string, error) {
activeConfigFile := filepath.Join(cfgDir, "config")
activeConfigData := t.env.FileContent(activeConfigFile)
if len(activeConfigData) == 0 {
return "", errors.New("NO ACTIVE CONFIG FOUND")
}
return activeConfigData, nil
}

View file

@ -0,0 +1,95 @@
package segments
import (
"path/filepath"
"testing"
"github.com/jandedobbeleer/oh-my-posh/src/mock"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)
func TestTalosctlSegment(t *testing.T) {
cases := []struct {
Case string
ActiveConfig string
ExpectedEnabled bool
ExpectedString string
}{
{
Case: "happy path",
ExpectedEnabled: true,
ActiveConfig: "context: context-name",
ExpectedString: "context-name",
},
{
Case: "no active config",
ExpectedEnabled: false,
},
{
Case: "empty config",
ActiveConfig: "",
ExpectedEnabled: false,
},
{
Case: "bad config",
ActiveConfig: "other-yaml: not-expected",
ExpectedEnabled: false,
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("Home").Return("home")
fcPath := filepath.Join("home", ".talos", "config")
env.On("FileContent", fcPath).Return(tc.ActiveConfig)
env.On("Error", mock2.Anything).Return()
talos := TalosCTL{
env: env,
}
talos.Enabled()
assert.Equal(t, tc.ExpectedEnabled, talos.Enabled())
if tc.ExpectedEnabled {
assert.Equal(t, tc.ExpectedString, renderTemplate(env, talos.Template(), talos), tc.Case)
}
}
}
func TestGetTalosctlActiveConfig(t *testing.T) {
cases := []struct {
Case string
ActiveConfig string
ExpectedString string
ExpectedError string
}{
{
Case: "happy path",
ActiveConfig: "context: context-name",
ExpectedString: "context: context-name",
},
{
Case: "no active config",
ActiveConfig: "",
ExpectedError: "NO ACTIVE CONFIG FOUND",
},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("Home").Return("home")
configPath := filepath.Join("home", ".talos")
contentPath := filepath.Join(configPath, "config")
env.On("FileContent", contentPath).Return(tc.ActiveConfig)
env.On("Error", mock2.Anything).Return()
talos := TalosCTL{
env: env,
}
got, err := talos.getActiveConfig(configPath)
assert.Equal(t, tc.ExpectedString, got, tc.Case)
if len(tc.ExpectedError) > 0 {
assert.EqualError(t, err, tc.ExpectedError, tc.Case)
} else {
assert.NoError(t, err, tc.Case)
}
}
}

View file

@ -105,7 +105,7 @@ func mockFilePresence(tc *testCase, ui5tooling *UI5Tooling, env *mock.MockedEnvi
}
if match {
if tc.DisplayMode == DisplayModeFiles && tc.WorkingDir == WorkingDirRoot {
if tc.DisplayMode == DisplayModeFiles && tc.WorkingDir == WorkingDirRoot { //nolint: gocritic
env.On("HasFiles", f).Return(true)
env.On("HasFileInParentDirs", f, uint(4)).Return(false)
// mode context, working dir != working dir root

View file

@ -110,7 +110,7 @@ func TestUnitySegment(t *testing.T) {
}
// 2021.9.20f1 is used in the test cases below as a fake Unity version.
// As such, it doesn't exist in the predfined map in unity.go. This
// As such, it doesn't exist in the predefined map in unity.go. This
// allows us to test the web request portion of the code, which is the
// fallback for obtaining a C# version.
func TestUnitySegmentCSharpWebRequest(t *testing.T) {

View file

@ -52,6 +52,7 @@ var (
ShellIntegration bool
RPrompt bool
CursorPositioning bool
PromptMark bool
)
func getExecutablePath(env platform.Environment) (string, error) {
@ -207,9 +208,18 @@ func PrintInit(env platform.Environment) string {
if env.Flags().Manual {
return "false"
}
return strconv.FormatBool(setting)
}
promptMark := func() string {
if PromptMark {
return "iterm2_prompt_mark"
}
return ""
}
shell := env.Flags().Shell
configFile := env.Flags().Config
@ -273,6 +283,7 @@ func PrintInit(env platform.Environment) string {
"::CURSOR::", strconv.FormatBool(CursorPositioning),
"::UPGRADE::", strconv.FormatBool(hasNotice),
"::UPGRADENOTICE::", notice,
"::PROMPT_MARK::", promptMark(),
).Replace(script)
}

View file

@ -23,16 +23,19 @@ function fish_prompt
::OMP:: print transient --config $POSH_THEME --shell fish --status $omp_status_cache --pipestatus="$omp_pipestatus_cache" --execution-time $omp_duration --stack-count $omp_stack_count --shell-version $FISH_VERSION --no-status=$omp_no_exit_code
return
end
set --global omp_status_cache $omp_status_cache_temp
set --global omp_pipestatus_cache $omp_pipestatus_cache_temp
set --global omp_stack_count (count $dirstack)
set --global omp_duration "$CMD_DURATION$cmd_duration"
set --global omp_no_exit_code false
# check if variable set, < 3.2 case
if set --query omp_lastcommand; and test "$omp_lastcommand" = ""
set omp_duration 0
set omp_no_exit_code true
end
# works with fish >=3.2
if set --query omp_last_status_generation; and test "$omp_last_status_generation" = "$status_generation"
set omp_duration 0
@ -41,16 +44,23 @@ function fish_prompt
# first execution - $status_generation is 0, $omp_last_status_generation is empty
set omp_no_exit_code true
end
if set --query status_generation
set --global --export omp_last_status_generation $status_generation
end
set_poshcontext
# validate if the user cleared the screen
set --local omp_cleared false
set --local last_command (history search --max 1)
if test "$last_command" = "clear"
set omp_cleared true
end
::PROMPT_MARK::
::OMP:: print primary --config $POSH_THEME --shell fish --status $omp_status_cache --pipestatus="$omp_pipestatus_cache" --execution-time $omp_duration --stack-count $omp_stack_count --shell-version $FISH_VERSION --cleared=$omp_cleared --no-status=$omp_no_exit_code
end

View file

@ -23,12 +23,15 @@ $env.PROMPT_MULTILINE_INDICATOR = (^::OMP:: print secondary $"--config=($env.POS
$env.PROMPT_COMMAND = { ||
# hack to set the cursor line to 1 when the user clears the screen
# this obviously isn't bulletproof, but it's a start
let clear = (history | last 1 | get 0.command) == "clear"
mut clear = false
if $nu.history-enabled {
$clear = (history | is-empty) or ((history | last 1 | get 0.command) == "clear")
}
^::OMP:: print primary $"--config=($env.POSH_THEME)" --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)" $"--execution-time=(posh_cmd_duration)" $"--status=($env.LAST_EXIT_CODE)" $"--terminal-width=(posh_width)" $"--cleared=($clear)"
}
$env.PROMPT_COMMAND_RIGHT = { ||
$env.PROMPT_COMMAND_RIGHT = { ||
^::OMP:: print right $"--config=($env.POSH_THEME)" --shell=nu $"--shell-version=($env.POSH_SHELL_VERSION)" $"--execution-time=(posh_cmd_duration)" $"--status=($env.LAST_EXIT_CODE)" $"--terminal-width=(posh_width)"
}

View file

@ -4,6 +4,7 @@ export POSH_PID=$$
export POWERLINE_COMMAND="oh-my-posh"
export CONDA_PROMPT_MODIFIER=false
export POSH_PROMPT_COUNT=0
export ZLE_RPROMPT_INDENT=0
# set secondary prompt
PS2="$(::OMP:: print secondary --config="$POSH_THEME" --shell=zsh)"

View file

@ -28,15 +28,16 @@ var (
"ShellVersion",
"UserName",
"HostName",
"Env",
"Data",
"Code",
"Env",
"OS",
"WSL",
"Segments",
"Templates",
"PromptCount",
"Segments",
"SHLVL",
"Templates",
"Var",
"Data",
}
)
@ -158,7 +159,7 @@ func (t *Text) cleanTemplate() {
continue
}
// end of a variable, needs to be appended
if !isKnownVariable(property) {
if !isKnownVariable(property) { //nolint: gocritic
result += ".Data" + property
} else if strings.HasPrefix(property, ".Segments") && !strings.HasSuffix(property, ".Contains") {
// as we can't provide a clean way to access the list

View file

@ -59,15 +59,16 @@ func Latest(env platform.Environment) (string, error) {
//
// The upgrade check is only performed every other week.
func Notice(env platform.Environment) (string, bool) {
// never validate when we install using the Windows Store
if env.Getenv("POSH_INSTALLER") == "ws" {
return "", false
}
// do not check when last validation was < 1 week ago
if _, OK := env.Cache().Get(CACHEKEY); OK {
return "", false
}
// never validate when we install using the Windows Store
if env.Getenv("POSH_INSTALLER") == "ws" {
return "", false
}
latest, err := Latest(env)
if err != nil {
return "", false

View file

@ -29,7 +29,7 @@
"style": "folder"
},
"style": "powerline",
"template": " \uf07b\uea9c{{ .Path }} ",
"template": " \uf07b\uea9c {{ .Path }} ",
"type": "path"
},
{
@ -59,7 +59,7 @@
"threshold": 0
},
"style": "diamond",
"template": " \ueba2{{ .FormattedMs }}\u2800",
"template": " \ueba2 {{ .FormattedMs }}\u2800",
"trailing_diamond": "\ue0b4",
"type": "executiontime"
}
@ -204,13 +204,14 @@
"background": "#b2bec3",
"foreground": "#222222",
"leading_diamond": "\ue0b6",
"trailing_diamond": "<transparent,background>\ue0b2</>",
"properties": {
"linux": "\ue712",
"macos": "\ue711",
"windows": "\ue70f"
},
"style": "diamond",
"template": " {{ if .WSL }}WSL at {{ end }}{{.Icon}}<#222222> \ue0b2</>",
"template": " {{ if .WSL }}WSL at {{ end }}{{.Icon}} ",
"type": "os"
},
{
@ -222,14 +223,14 @@
],
"foreground": "#262626",
"invert_powerline": true,
"leading_diamond": "\ue0b2",
"powerline_symbol": "\ue0b2",
"properties": {
"charged_icon": "\uf240 ",
"charging_icon": "\uf1e6 ",
"discharging_icon": "\ue234 "
},
"style": "diamond",
"template": " {{ if not .Error }}{{ .Icon }}{{ .Percentage }}{{ end }}{{ .Error }}\uf295 <#262626>\ue0b2</>",
"style": "powerline",
"template": " {{ if not .Error }}{{ .Icon }}{{ .Percentage }}{{ end }}{{ .Error }}\uf295 ",
"type": "battery"
},
{

View file

@ -7,10 +7,12 @@
"cloud-text-amazon": "#4285F4",
"cloud-text-azure": "#4285F4",
"cloud-text-gcp": "#4285F4",
"cloud-text-firebase": "#FFA000",
"error-background": "#dd0033",
"error-text": "#242424",
"git-text": "#238636",
"kubernetes-text": "#FFBB00",
"talosctl-text": "#FF6C00",
"symbol-color": "#ffffff",
"timer-text": "#dd0033"
},
@ -38,6 +40,14 @@
"template": "<p:symbol-color>\uf308</> {{.Context}}{{if .Namespace}} \uf061 {{.Namespace}}{{end}}",
"type": "kubectl"
},
{
"background": "p:background-color",
"foreground": "p:talosctl-text",
"style": "powerline",
"powerline_symbol": "\ue0b4",
"template": "<p:symbol-color>\udb84\udcfe</> {{.Context}}",
"type": "talosctl"
},
{
"background": "p:background-color",
"foreground": "p:cloud-text-amazon",
@ -76,6 +86,14 @@
"template": " <p:symbol-color>\ue7b2</> {{ .Project }}",
"type": "gcp"
},
{
"background": "p:background-color",
"foreground": "p:cloud-text-firebase",
"style": "powerline",
"powerline_symbol": "\ue0b4",
"template": " <p:symbol-color>\udb82\udd67</> {{ .Project }}",
"type": "firebase"
},
{
"background": "p:background-color",
"foreground": "p:git-text",

View file

@ -1,4 +1,4 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json
final_space: true
blocks:
- type: prompt
alignment: left
@ -27,7 +27,7 @@ blocks:
opensuse:
raspbian:
ubuntu:
windows:
windows: 󰍲
wsl:
wsl_separator:
template: " {{ if .WSL }}{{ end }}{{.Icon}}═"
@ -42,7 +42,7 @@ blocks:
powerline_symbol:
background: magenta
foreground: black
template: " {{ if .SSHSession }} {{ end }}{{ .UserName }}@{{ .HostName }} "
template: " {{ if .SSHSession }}󰌘 {{ end }}{{ .UserName }}@{{ .HostName }} "
- type: angular
style: powerline
powerline_symbol:
@ -50,7 +50,7 @@ blocks:
foreground: black
properties:
fetch_version: true
template: " {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
template: " 󰚲 {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
- type: aws
style: powerline
powerline_symbol:
@ -83,7 +83,7 @@ blocks:
foreground: black
properties:
fetch_version: true
template: " {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
template: " 󰟓 {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
- type: java
style: powerline
powerline_symbol:
@ -103,7 +103,7 @@ blocks:
powerline_symbol:
background: lightYellow
foreground: black
template: " {{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}} "
template: " 󰠳 {{.Context}}{{if .Namespace}} :: {{.Namespace}}{{end}} "
- type: node
style: powerline
powerline_symbol:
@ -111,7 +111,7 @@ blocks:
foreground: black
properties:
fetch_version: true
template: " {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} "
template: " 󰎙 {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} "
- type: php
style: powerline
powerline_symbol:
@ -192,7 +192,7 @@ blocks:
fetch_status: true
fetch_upstream_icon: true
fetch_worktree_count: true
template: "{{ .UpstreamIcon }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }}  {{ .Working.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Staging.Changed }}  {{ .Staging.String }}{{ end }}{{ if gt .StashCount 0 }} {{ .StashCount }}{{ end }}"
template: "{{ .UpstreamIcon }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }}  {{ .Working.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Staging.Changed }}  {{ .Staging.String }}{{ end }}{{ if gt .StashCount 0 }} 󰆓 {{ .StashCount }}{{ end }}"
- type: prompt
alignment: left
newline: true
@ -210,7 +210,7 @@ blocks:
foreground: black
properties:
always_enabled: true
template: " {{ if gt .Code 0 }}{{ else }}{{ end }}{{ if eq ( gt .Code 0 ) .Segments.Path.Writable}} {{ end }}"
template: " {{ if gt .Code 0 }}󰋔 {{ else }} {{ end }}{{ if eq ( gt .Code 0 ) .Segments.Path.Writable}} {{ end }}"
- type: path
style: powerline
powerline_symbol:
@ -221,10 +221,10 @@ blocks:
properties:
style: agnoster_short
folder_icon:
folder_separator_icon: " "
home_icon:
folder_separator_icon: " 󰁕 "
home_icon: 󰋜
max_depth: 3
template: "{{ if eq ( gt .Code 0 ) .Writable }} {{ end }} {{ .Path }}{{ if not .Writable }} {{ end }}{{ if and .Writable .Root }} {{end}}"
template: "{{ if eq ( gt .Code 0 ) .Writable }} {{ end }} {{ .Path }}{{ if not .Writable }} 󰍁 {{ end }}{{ if and .Writable .Root }} {{end}} "
- type: text
style: diamond
trailing_diamond:
@ -234,7 +234,7 @@ blocks:
foreground: white
properties:
root_icon:
template: "{{ if and .Root ( not .Segments.Path.Writable ) }} {{ end }}{{ if and .Root .Segments.Path.Writable }}  {{ end }} \b"
template: "{{ if not .Root }}\u2800{{ end }}{{ if and .Root ( not .Segments.Path.Writable ) }} {{ end }}{{ if and .Root .Segments.Path.Writable }}  {{ end }}"
console_title_template: "{{ .Folder }}"
palette:
black: "#1B1A23"

View file

@ -52,7 +52,7 @@ blocks:
always_enabled: true
style: round
style: powerline
template: " {{ .FormattedMs }} "
template: " 󰔟{{ .FormattedMs }} "
type: executiontime
# Exit code
@ -84,14 +84,14 @@ blocks:
foreground: p:white
leading_diamond:
properties:
charged_icon:
charging_icon:
discharging_icon: " "
charged_icon: 󰂄
charging_icon: 󰂄
discharging_icon: "󰁹 "
style: diamond
template: " {{.Templates }}{{ .Percentage}} <transparent></>"
template: " {{.Templates }}{{ .Percentage}}󰏰 <transparent></>"
templates:
- '{{if eq "Discharging" .State.String}}{{if lt .Percentage 11}}{{else if lt .Percentage 21}}{{else if lt .Percentage 31}}{{else if lt .Percentage 41}}{{else if lt .Percentage 51}}{{else if lt .Percentage 61}}{{else if lt .Percentage 71}}{{else if lt .Percentage 81}}{{else if lt .Percentage 91}}{{else}}{{end}}{{end}}'
- '{{if eq "Charging" .State.String}}{{ if lt .Percentage 21}}{{else if lt .Percentage 31}}{{else if lt .Percentage 41}}{{else if lt .Percentage 61}}{{else if lt .Percentage 81}}{{else if lt .Percentage 91}}{{else}}{{end}}{{end}}'
- '{{if eq "Discharging" .State.String}}{{if lt .Percentage 11}}󰁺{{else if lt .Percentage 21}}󰁻{{else if lt .Percentage 31}}󰁼{{else if lt .Percentage 41}}󰁽{{else if lt .Percentage 51}}󰁾{{else if lt .Percentage 61}}󰁾{{else if lt .Percentage 71}}󰂀{{else if lt .Percentage 81}}󰂁{{else if lt .Percentage 91}}󰂂{{else}}󰁹{{end}}{{end}}'
- '{{if eq "Charging" .State.String}}{{ if lt .Percentage 21}}󰂆{{else if lt .Percentage 31}}󰂇{{else if lt .Percentage 41}}󰂈{{else if lt .Percentage 61}}󰂉{{else if lt .Percentage 81}}󰂊{{else if lt .Percentage 91}}󰂋{{else}}󰂄{{end}}{{end}}'
templates_logic: first_match
type: battery
@ -117,7 +117,7 @@ blocks:
if ! res=$(curl -s wttr.in -Gsm2 --data-urlencode "format=j1"); then
echo "$now"
date +'%s' >/tmp/last_update.w
echo >/tmp/weather.w
echo 󰖪 >/tmp/weather.w
rm /tmp/w_lock.w
exit 0
fi
@ -125,7 +125,7 @@ blocks:
temp=$(jq -r .temp_C <<<"$res")
feels_like=$(jq -r .FeelsLikeC <<<"$res")
[[ $(bc -l <<<"$feels_like!=$temp") -eq 1 ]] && feels_string=" feels $feels_like°C"
WWC_MAP='{"113":"Sunny","116":"PartlyCloudy","119":"Cloudy","122":"VeryCloudy","143":"Fog","176":"LightShowers","179":"LightSleetShowers","182":"LightSleet","185":"LightSleet","200":"ThunderyShowers","227":"LightSnow","230":"HeavySnow","248":"Fog","260":"Fog","263":"LightShowers","266":"LightRain","281":"LightSleet","284":"LightSleet","293":"LightRain","296":"LightRain","299":"HeavyShowers","302":"HeavyRain","305":"HeavyShowers","308":"HeavyRain","311":"LightSleet","314":"LightSleet","317":"LightSleet","320":"LightSnow","323":"LightSnowShowers","326":"LightSnowShowers","329":"HeavySnow","332":"HeavySnow","335":"HeavySnowShowers","338":"HeavySnow","350":"LightSleet","353":"LightShowers","356":"HeavyShowers","359":"HeavyRain","362":"LightSleetShowers","365":"LightSleetShowers","368":"LightSnowShowers","371":"HeavySnowShowers","374":"LightSleetShowers","377":"LightSleet","386":"ThunderyShowers","389":"ThunderyHeavyRain","392":"ThunderySnowShowers","395":"HeavySnowShowers"}';NF_DAY_MAP='{"Unknown":"","Cloudy":"","Fog":"","HeavyRain":"","HeavyShowers":"","HeavySnow":"","HeavySnowShowers":"","LightRain":"","LightShowers":"","LightSleet":"","LightSleetShowers":"","LightSnow":"","LightSnowShowers":"","PartlyCloudy":"","Sunny":"","ThunderyHeavyRain":"","ThunderyShowers":"","ThunderySnowShowers":"","VeryCloudy":""}';NF_NIGHT_MAP='{"Unknown":"","Cloudy":"","Fog":"","HeavyRain":"","HeavyShowers":"","HeavySnow":"","HeavySnowShowers":"","LightRain":"","LightShowers":"","LightSleet":"","LightSleetShowers":"","LightSnow":"","LightSnowShowers":"","PartlyCloudy":"","Sunny":"","ThunderyHeavyRain":"","ThunderyShowers":"","ThunderySnowShowers":"","VeryCloudy":""}'
WWC_MAP='{"113":"Sunny","116":"PartlyCloudy","119":"Cloudy","122":"VeryCloudy","143":"Fog","176":"LightShowers","179":"LightSleetShowers","182":"LightSleet","185":"LightSleet","200":"ThunderyShowers","227":"LightSnow","230":"HeavySnow","248":"Fog","260":"Fog","263":"LightShowers","266":"LightRain","281":"LightSleet","284":"LightSleet","293":"LightRain","296":"LightRain","299":"HeavyShowers","302":"HeavyRain","305":"HeavyShowers","308":"HeavyRain","311":"LightSleet","314":"LightSleet","317":"LightSleet","320":"LightSnow","323":"LightSnowShowers","326":"LightSnowShowers","329":"HeavySnow","332":"HeavySnow","335":"HeavySnowShowers","338":"HeavySnow","350":"LightSleet","353":"LightShowers","356":"HeavyShowers","359":"HeavyRain","362":"LightSleetShowers","365":"LightSleetShowers","368":"LightSnowShowers","371":"HeavySnowShowers","374":"LightSleetShowers","377":"LightSleet","386":"ThunderyShowers","389":"ThunderyHeavyRain","392":"ThunderySnowShowers","395":"HeavySnowShowers"}';NF_DAY_MAP='{"Unknown":"󰚌","Cloudy":"","Fog":"","HeavyRain":"","HeavyShowers":"","HeavySnow":"","HeavySnowShowers":"","LightRain":"","LightShowers":"","LightSleet":"","LightSleetShowers":"","LightSnow":"","LightSnowShowers":"","PartlyCloudy":"","Sunny":"","ThunderyHeavyRain":"","ThunderyShowers":"","ThunderySnowShowers":"","VeryCloudy":""}';NF_NIGHT_MAP='{"Unknown":"󰚌","Cloudy":"","Fog":"","HeavyRain":"","HeavyShowers":"","HeavySnow":"","HeavySnowShowers":"","LightRain":"","LightShowers":"","LightSleet":"","LightSleetShowers":"","LightSnow":"","LightSnowShowers":"","PartlyCloudy":"","Sunny":"","ThunderyHeavyRain":"","ThunderyShowers":"","ThunderySnowShowers":"","VeryCloudy":""}'
cur_h="$(date +'%H')" ;wwc="$(jq '.weatherCode' <<<"$res")"
if [[ $cur_h -gt 18 || $cur_h -lt 6 ]]; then icon=$(jq -r ".$(echo "$WWC_MAP" | jq -r ".$wwc")" <<<"$NF_DAY_MAP")
else icon=$(jq -r ".$(echo "$WWC_MAP" | jq -r ".$wwc")" <<<"$NF_NIGHT_MAP"); fi

Some files were not shown because too many files have changed in this diff Show more