mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-11-09 20:44:03 -08:00
Merge branch 'main' into main
This commit is contained in:
commit
846a9febc1
|
@ -759,7 +759,8 @@
|
|||
"avatar_url": "https://avatars.githubusercontent.com/u/598065?v=4",
|
||||
"profile": "http://www.terrasoft.gr/",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"design"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -1484,7 +1485,9 @@
|
|||
"avatar_url": "https://avatars.githubusercontent.com/u/10334184?v=4",
|
||||
"profile": "https://github.com/LNKLEO",
|
||||
"contributions": [
|
||||
"code"
|
||||
"code",
|
||||
"design",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -2426,6 +2429,261 @@
|
|||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "herbygillot",
|
||||
"name": "Herby Gillot",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/618376?v=4",
|
||||
"profile": "https://github.com/herbygillot",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "arjan-s",
|
||||
"name": "arjan-s",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10400299?v=4",
|
||||
"profile": "https://github.com/arjan-s",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "0323pin",
|
||||
"name": "pin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/90570748?v=4",
|
||||
"profile": "https://github.com/0323pin",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "FireIsGood",
|
||||
"name": "FireIsGood",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/109556932?v=4",
|
||||
"profile": "http://fireis.dev",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Joxtacy",
|
||||
"name": "Jesper Hasselquist",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10127673?v=4",
|
||||
"profile": "https://github.com/Joxtacy",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aaronpowell",
|
||||
"name": "Aaron Powell",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/434140?v=4",
|
||||
"profile": "https://www.aaron-powell.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Dartypier",
|
||||
"name": "Jacopo Zecchi",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22201626?v=4",
|
||||
"profile": "https://github.com/Dartypier",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "rose-m",
|
||||
"name": "Michael Rose",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4354632?v=4",
|
||||
"profile": "https://github.com/rose-m",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "denehoffman",
|
||||
"name": "Nathaniel D. Hoffman",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/36977879?v=4",
|
||||
"profile": "http://denehoffman.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "michaelschwobe",
|
||||
"name": "Michael Schwobe",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/926242?v=4",
|
||||
"profile": "https://schwobe.dev",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nibodhika",
|
||||
"name": "Nibodhika",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/729967?v=4",
|
||||
"profile": "https://github.com/Nibodhika",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sassdawe",
|
||||
"name": "David Sass",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10754765?v=4",
|
||||
"profile": "http://davidsass.io",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "carehart",
|
||||
"name": "Charlie Arehart",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/389746?v=4",
|
||||
"profile": "http://www.carehart.org",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "aramikuto",
|
||||
"name": "Aleksandr Kondrashov",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/116561995?v=4",
|
||||
"profile": "https://github.com/aramikuto",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kimsey0",
|
||||
"name": "Jacob Bundgaard",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/984760?v=4",
|
||||
"profile": "https://jacobbundgaard.dk",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "ThisaruGuruge",
|
||||
"name": "Thisaru Guruge",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/40016057?v=4",
|
||||
"profile": "https://thisaru.me",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "edwin-shdw",
|
||||
"name": "Edwin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/62764562?v=4",
|
||||
"profile": "https://github.com/edwin-shdw",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "jcdickinson",
|
||||
"name": "Jonathan Dickinson",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/522465?v=4",
|
||||
"profile": "https://dickinson.id",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "po1o",
|
||||
"name": "Polo-François Poli",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5702825?v=4",
|
||||
"profile": "https://github.com/po1o",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EDIflyer",
|
||||
"name": "EDIflyer",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13610277?v=4",
|
||||
"profile": "https://github.com/EDIflyer",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "felipebz",
|
||||
"name": "Felipe Zorzo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13829?v=4",
|
||||
"profile": "https://felipezorzo.com.br",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "DeepSpace2",
|
||||
"name": "Adi Vaknin",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/6841988?v=4",
|
||||
"profile": "https://github.com/DeepSpace2",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "EladLeev",
|
||||
"name": "Elad Leev",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/835319?v=4",
|
||||
"profile": "https://leevs.dev/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Soyvolon",
|
||||
"name": "Bounds",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/16871668?v=4",
|
||||
"profile": "https://github.com/Soyvolon",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Yash-Garg",
|
||||
"name": "Yash Garg",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/33605526?v=4",
|
||||
"profile": "http://yashgarg.dev",
|
||||
"contributions": [
|
||||
"code",
|
||||
"design",
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "sarpuser",
|
||||
"name": "Sarp User",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/23362324?v=4",
|
||||
"profile": "https://github.com/sarpuser",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
|
|
@ -52,9 +52,6 @@ RUN pwsh -Command Install-Module posh-git -Scope AllUsers -Force; \
|
|||
# add the oh-my-posh path to the PATH variable
|
||||
ENV PATH "$PATH:/home/${USERNAME}/bin"
|
||||
|
||||
# Can be used to override the devcontainer prompt default theme:
|
||||
ENV POSH_THEME="/workspaces/oh-my-posh/themes/jandedobbeleer.omp.json"
|
||||
|
||||
# Deploy oh-my-posh prompt to Powershell:
|
||||
COPY Microsoft.PowerShell_profile.ps1 /home/${USERNAME}/.config/powershell/Microsoft.PowerShell_profile.ps1
|
||||
|
||||
|
@ -67,10 +64,10 @@ RUN chmod 777 -R /home/${USERNAME}/.config
|
|||
|
||||
# Override vscode's own Bash prompt with oh-my-posh:
|
||||
RUN sed -i 's/^__bash_prompt$/#&/' /home/${USERNAME}/.bashrc && \
|
||||
echo "eval \"\$(oh-my-posh init bash --config $POSH_THEME)\"" >> /home/${USERNAME}/.bashrc
|
||||
echo "eval \"\$(oh-my-posh init bash)\"" >> /home/${USERNAME}/.bashrc
|
||||
|
||||
# Override vscode's own ZSH prompt with oh-my-posh:
|
||||
RUN echo "eval \"\$(oh-my-posh init zsh --config $POSH_THEME)\"" >> /home/${USERNAME}/.zshrc
|
||||
RUN echo "eval \"\$(oh-my-posh init zsh)\"" >> /home/${USERNAME}/.zshrc
|
||||
|
||||
# Set container timezone:
|
||||
ARG TZ="UTC"
|
||||
|
|
|
@ -6,6 +6,4 @@ Import-Module Terminal-Icons
|
|||
Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete
|
||||
|
||||
$env:POSH_GIT_ENABLED=$true
|
||||
oh-my-posh init pwsh --config $env:POSH_THEME | Invoke-Expression
|
||||
|
||||
# NOTE: You can override the above env var from the devcontainer.json "args" under the "build" key.
|
||||
oh-my-posh init pwsh | Invoke-Expression
|
||||
|
|
|
@ -1,4 +1,2 @@
|
|||
# Activate oh-my-posh prompt:
|
||||
oh-my-posh init fish --config $POSH_THEME | source
|
||||
|
||||
# NOTE: You can override the above env vars from the devcontainer.json "args" under the "build" key.
|
||||
oh-my-posh init fish | source
|
||||
|
|
|
@ -8,10 +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.22.3-bullseye",
|
||||
|
||||
// Options:
|
||||
"POSH_THEME": "/workspaces/oh-my-posh/themes/jandedobbeleer.omp.json",
|
||||
"VARIANT": "1-1.22-bullseye",
|
||||
|
||||
// Override me with your own timezone:
|
||||
"TZ": "UTC",
|
||||
|
@ -23,7 +20,16 @@
|
|||
"PS_VERSION": "7.2.7"
|
||||
}
|
||||
},
|
||||
"runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"],
|
||||
"runArgs": [
|
||||
"--cap-add=SYS_PTRACE",
|
||||
"--security-opt",
|
||||
"seccomp=unconfined",
|
||||
"--security-opt",
|
||||
"label=disable"
|
||||
],
|
||||
"containerEnv": {
|
||||
"HOME": "/home/vscode"
|
||||
},
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
|
@ -54,6 +60,7 @@
|
|||
"terminal.integrated.defaultProfile.linux": "pwsh",
|
||||
"terminal.integrated.defaultProfile.windows": "pwsh",
|
||||
"terminal.integrated.defaultProfile.osx": "pwsh",
|
||||
"terminal.integrated.shellIntegration.enabled": false,
|
||||
"tasks.statusbar.default.hide": true
|
||||
},
|
||||
"extensions": [
|
||||
|
|
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Bug Report
|
||||
name: 🐛 Bug Report
|
||||
description: File a bug report
|
||||
labels: ["🐛 bug"]
|
||||
assignees:
|
||||
|
|
27
.github/ISSUE_TEMPLATE/docs.yml
vendored
Normal file
27
.github/ISSUE_TEMPLATE/docs.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
name: 📖 Documentation
|
||||
description: Suggest a change to the documentation
|
||||
labels: ["📖 docs"]
|
||||
assignees:
|
||||
- jandedobbeleer
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to request this improvement!
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
- type: textarea
|
||||
id: enhancement-request
|
||||
attributes:
|
||||
label: What would you like to see changed/added?
|
||||
description: Try to give some examples or text to make it really clear!
|
||||
placeholder: Tell us what you would like to see!
|
||||
value: "This could change in the documentation!"
|
||||
validations:
|
||||
required: true
|
27
.github/ISSUE_TEMPLATE/enhancement.yml
vendored
Normal file
27
.github/ISSUE_TEMPLATE/enhancement.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
name: 🤩 Enhancement
|
||||
description: Suggest a change to an existing feature
|
||||
labels: ["🤩 enhancement"]
|
||||
assignees:
|
||||
- jandedobbeleer
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to request this improvement!
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
- type: textarea
|
||||
id: enhancement-request
|
||||
attributes:
|
||||
label: What would you like to see changed?
|
||||
description: Try to give some examples to make it really clear!
|
||||
placeholder: Tell us what you would like to see!
|
||||
value: "This feature would benefit from this!"
|
||||
validations:
|
||||
required: true
|
6
.github/ISSUE_TEMPLATE/feat.yml
vendored
6
.github/ISSUE_TEMPLATE/feat.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Feature Request
|
||||
name: 🚀 Feature Request
|
||||
description: Request a new feature
|
||||
labels: ["🚀 feat"]
|
||||
assignees:
|
||||
|
@ -19,8 +19,8 @@ body:
|
|||
- type: textarea
|
||||
id: feature-request
|
||||
attributes:
|
||||
label: What would you like to see changed/added?
|
||||
description: Try to give some examples to make it really clear!
|
||||
label: What would you like to see added?
|
||||
description: Try to give some examples to make it really clear.
|
||||
placeholder: Tell us what you would like to see!
|
||||
value: "Something new and amazing!"
|
||||
validations:
|
||||
|
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -12,7 +12,7 @@
|
|||
Tips:
|
||||
|
||||
If you're not comfortable with working with Git,
|
||||
we're working a guide (https://ohmyposh.dev/docs/contributing_git) to help you out.
|
||||
we're working a guide (https://ohmyposh.dev/docs/contributing/git) to help you out.
|
||||
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.
|
||||
|
||||
-->
|
||||
|
|
2
.github/workflows/android.yml
vendored
2
.github/workflows/android.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
container: ghcr.io/jandedobbeleer/golang-android-container:latest
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Build
|
||||
run: |
|
||||
VERSION=$(echo "${{ github.event.release.name }}" | cut -c2-)
|
||||
|
|
6
.github/workflows/build_code.yml
vendored
6
.github/workflows/build_code.yml
vendored
|
@ -17,18 +17,18 @@ jobs:
|
|||
shell: pwsh
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Install Go 🗳
|
||||
uses: ./.github/workflows/composite/bootstrap-go
|
||||
- name: Run GoReleaser 🚀
|
||||
uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811
|
||||
uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: build --clean --snapshot --skip=post-hooks --skip=before
|
||||
workdir: src
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a
|
||||
with:
|
||||
name: builds
|
||||
retention-days: 1
|
||||
|
|
2
.github/workflows/close_themes_pr.yml
vendored
2
.github/workflows/close_themes_pr.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Check and close 🔐
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
|
||||
with:
|
||||
|
|
6
.github/workflows/code.yml
vendored
6
.github/workflows/code.yml
vendored
|
@ -20,14 +20,14 @@ jobs:
|
|||
working-directory: ${{ github.workspace }}/src
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32
|
||||
with:
|
||||
go-version: 1.21
|
||||
cache-dependency-path: src/go.sum
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Golang CI
|
||||
uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64
|
||||
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86
|
||||
with:
|
||||
working-directory: src
|
||||
- name: Unit Tests
|
||||
|
|
10
.github/workflows/codeql.yml
vendored
10
.github/workflows/codeql.yml
vendored
|
@ -20,17 +20,17 @@ jobs:
|
|||
working-directory: ${{ github.workspace }}/src
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32
|
||||
with:
|
||||
go-version: 1.21
|
||||
cache-dependency-path: src/go.sum
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276
|
||||
uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa
|
||||
with:
|
||||
languages: go
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@9fdb3e49720b44c48891d036bb502feb25684276
|
||||
uses: github/codeql-action/autobuild@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276
|
||||
uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa
|
||||
|
|
|
@ -10,3 +10,4 @@ runs:
|
|||
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
|
||||
with:
|
||||
go-version: '1.22.3'
|
||||
cache-dependency-path: src/go.sum
|
||||
|
|
8
.github/workflows/docs.yml
vendored
8
.github/workflows/docs.yml
vendored
|
@ -18,16 +18,16 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
name: Build and Deploy
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
with:
|
||||
submodules: true
|
||||
persist-credentials: false
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32
|
||||
with:
|
||||
go-version: 1.21
|
||||
cache-dependency-path: src/go.sum
|
||||
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8
|
||||
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b
|
||||
with:
|
||||
node-version: 20.9.0
|
||||
# Create Kind cluster to have a Kubernetes context for cloud-native-azure theme
|
||||
|
@ -44,7 +44,7 @@ jobs:
|
|||
- name: Set default Kubernetes namespace
|
||||
run: |
|
||||
kubectl config set-context posh --namespace demo
|
||||
- uses: azure/login@6b2456866fc08b011acb422a92a4aa20e2c4de32
|
||||
- uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_CREDENTIALS }}
|
||||
- name: Build oh-my-posh 🔧
|
||||
|
|
4
.github/workflows/gomod.yml
vendored
4
.github/workflows/gomod.yml
vendored
|
@ -10,12 +10,12 @@ jobs:
|
|||
working-directory: ${{ github.workspace }}/src
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32
|
||||
with:
|
||||
go-version: 1.21
|
||||
cache-dependency-path: src/go.sum
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Check for unused dependencies
|
||||
run: |
|
||||
go mod tidy
|
||||
|
|
2
.github/workflows/inno.yml
vendored
2
.github/workflows/inno.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
working-directory: ${{ github.workspace }}/packages/inno
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Build installer 📦
|
||||
id: build
|
||||
env:
|
||||
|
|
2
.github/workflows/markdown.yml
vendored
2
.github/workflows/markdown.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Lint files
|
||||
uses: articulate/actions-markdownlint@17b8abe7407cd17590c006ecc837c35e1ac3ed83
|
||||
with:
|
||||
|
|
2
.github/workflows/merge_contributions_pr.yml
vendored
2
.github/workflows/merge_contributions_pr.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Check and merge ⛙
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
|
||||
with:
|
||||
|
|
47
.github/workflows/release.yml
vendored
47
.github/workflows/release.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
|||
skipped: ${{ steps.changelog.outputs.skipped }}
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Create changelog ✍️
|
||||
id: changelog
|
||||
uses: TriPSs/conventional-changelog-action@3a392e9aa44a72686b0fc13259a90d287dd0877c
|
||||
|
@ -32,16 +32,14 @@ jobs:
|
|||
artifacts:
|
||||
needs: changelog
|
||||
if: ${{ needs.changelog.outputs.skipped == 'false' }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
COSIGN_KEY_LOCATION: "/tmp/cosign.key"
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
working-directory: ${{ github.workspace }}/src
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Install Go 🗳
|
||||
uses: ./.github/workflows/composite/bootstrap-go
|
||||
- name: Tag HEAD 😸
|
||||
|
@ -49,25 +47,40 @@ jobs:
|
|||
git config --global user.name "GitHub Actions"
|
||||
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@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20
|
||||
with:
|
||||
cosign-release: 'v1.4.0'
|
||||
- name: Private Key 🔐
|
||||
- name: Prerequisites 🔐
|
||||
run: |
|
||||
$PSDefaultParameterValues['Out-File:Encoding']='UTF8'
|
||||
$env:COSIGN_KEY > $env:COSIGN_KEY_LOCATION
|
||||
|
||||
$shaSigningKeyLocation = Join-Path -Path $env:RUNNER_TEMP -ChildPath sha_signing_key.pem
|
||||
$env:SIGNING_KEY > $shaSigningKeyLocation
|
||||
Write-Output "SHA_SIGNING_KEY_LOCATION=$shaSigningKeyLocation" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
# create a base64 encoded value of your certificate using
|
||||
# [convert]::ToBase64String((Get-Content -path "certificate.pfx" -AsByteStream))
|
||||
$pfxPath = Join-Path -Path $env:RUNNER_TEMP -ChildPath "code_signing_cert.pfx"
|
||||
$encodedBytes = [System.Convert]::FromBase64String($env:SIGNING_CERTIFICATE)
|
||||
Set-Content -Path $pfxPath -Value $encodedBytes -AsByteStream
|
||||
Write-Output "SIGNING_CERTIFICATE_LOCATION=$pfxPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
# requires Windows Dev Kit 10.0.22621.0
|
||||
$signtool = 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x86/signtool.exe'
|
||||
Write-Output "SIGNTOOL=$signtool" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
|
||||
# openssl
|
||||
$openssl = 'C:/Program Files/Git/usr/bin/openssl.exe'
|
||||
Write-Output "OPENSSL=$openssl" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
env:
|
||||
COSIGN_KEY: ${{secrets.COSIGN_KEY}}
|
||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||
SIGNING_CERTIFICATE: ${{ secrets.CERTIFICATE }}
|
||||
- name: Run GoReleaser 🚀
|
||||
uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811
|
||||
uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: build --clean
|
||||
args: release --clean --skip publish
|
||||
workdir: src
|
||||
env:
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }}
|
||||
- name: Zip theme files 🤐
|
||||
run: |
|
||||
$compress = @{
|
||||
|
@ -83,9 +96,8 @@ jobs:
|
|||
$zipHash = Get-FileHash $_.FullName -Algorithm SHA256
|
||||
$zipHash.Hash | Out-File -Encoding 'UTF8' "./dist/$($_.Name).sha256"
|
||||
}
|
||||
shell: pwsh
|
||||
- name: Release 🎓
|
||||
uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87
|
||||
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
|
||||
with:
|
||||
tag_name: ${{ needs.changelog.outputs.tag }}
|
||||
body: ${{ needs.changelog.outputs.body }}
|
||||
|
@ -94,3 +106,4 @@ jobs:
|
|||
files: |
|
||||
src/dist/posh-*
|
||||
src/dist/themes.*
|
||||
src/dist/checksums.*
|
||||
|
|
2
.github/workflows/scoop.yml
vendored
2
.github/workflows/scoop.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
|||
working-directory: ${{ github.workspace }}/packages/scoop
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Update Template ✍️
|
||||
run: |
|
||||
./build.ps1 -Version ${{ github.event.inputs.version }}
|
||||
|
|
2
.github/workflows/winget.yml
vendored
2
.github/workflows/winget.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
WINGETCREATE_TOKEN: ${{ secrets.WINGETCREATE_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code 👋
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
|
||||
- name: Create manifest and submit PR 📦
|
||||
run: |
|
||||
./build.ps1 -Version "${{ github.event.inputs.version }}" -Token $env:WINGETCREATE_TOKEN
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
|||
|
||||
.fleet/
|
||||
src/test/umbraco/obj/
|
||||
src/keys
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,go,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,go,visualstudiocode
|
||||
|
|
26
.vscode/launch.json
vendored
26
.vscode/launch.json
vendored
|
@ -145,6 +145,27 @@
|
|||
"git"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Upgrade",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceRoot}/src",
|
||||
"args": [
|
||||
"upgrade"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Font install",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceRoot}/src",
|
||||
"args": [
|
||||
"font",
|
||||
"install"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
|
@ -172,13 +193,14 @@
|
|||
"envFile": "${workspaceFolder}/website/.env"
|
||||
},
|
||||
{
|
||||
"name": "Cache",
|
||||
"name": "Cache clear",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceRoot}/src",
|
||||
"args": [
|
||||
"cache"
|
||||
"cache",
|
||||
"clear"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -32,7 +32,6 @@ Fish and PowerShell, the latter of which is the default.
|
|||
1. Open the `.devcontainer/devcontainer.json` file and in the "*build*" section modify:
|
||||
|
||||
- `TZ`: with [your own timezone][timezones]
|
||||
- `DEFAULT_POSH_THEME`: with [your preferred theme][themes]
|
||||
|
||||
2. Summon the Command Panel (Ctrl+Shift+P) and select `Codespaces: Rebuild Container`
|
||||
to rebuild your devcontainer. (This should take just a few seconds.)
|
||||
|
@ -59,4 +58,3 @@ if you do `Codespaces: Rebuild Container` again, you'll be back to the latest st
|
|||
[codespaces-link]: https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=175405157
|
||||
[devcontainer-ext]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
|
||||
[timezones]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
[themes]: https://ohmyposh.dev/docs/themes
|
||||
|
|
34
configurations/configuration.dsc.yaml
Normal file
34
configurations/configuration.dsc.yaml
Normal file
|
@ -0,0 +1,34 @@
|
|||
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
|
||||
properties:
|
||||
resources:
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
directives:
|
||||
description: Install Visual Studio Code
|
||||
allowPrerelease: false
|
||||
settings:
|
||||
id: Microsoft.VisualStudioCode
|
||||
source: winget
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
id: golang
|
||||
directives:
|
||||
description: Install Golang
|
||||
allowPrerelease: false
|
||||
settings:
|
||||
id: GoLang.Go
|
||||
source: winget
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
dependsOn: [golang]
|
||||
directives:
|
||||
description: Install golangci-lint
|
||||
allowPrerelease: false
|
||||
settings:
|
||||
id: GolangCI.golangci-lint
|
||||
source: winget
|
||||
- resource: Microsoft.WinGet.DSC/WinGetPackage
|
||||
directives:
|
||||
description: Install NodeJS
|
||||
allowPrerelease: false
|
||||
settings:
|
||||
id: OpenJS.NodeJS
|
||||
source: winget
|
||||
configurationVersion: 0.2.0
|
|
@ -1,4 +0,0 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnLQ9sj71/ignxPXEa072vezEZf5D
|
||||
X1fO3KuoFCtzYTLc/c3rwVGtIdzc02qUoXABysJ0Ok8lsmWvwKvC7yWblg==
|
||||
-----END PUBLIC KEY-----
|
|
@ -10,10 +10,10 @@ Param
|
|||
|
||||
# Get signing certificate
|
||||
$pfxPath = Join-Path -Path $env:RUNNER_TEMP -ChildPath "cert.pfx"
|
||||
$signtool = 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.22000.0/x86/signtool.exe'
|
||||
$signtool = 'C:/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x86/signtool.exe'
|
||||
# create a base64 encoded value of your certificate using
|
||||
# [convert]::ToBase64String((Get-Content -path "certificate.pfx" -AsByteStream))
|
||||
# requires Windows Dev Kit 10.0.22000.0
|
||||
# requires Windows Dev Kit 10.0.22621.0
|
||||
$encodedBytes = [System.Convert]::FromBase64String($env:CERTIFICATE)
|
||||
Set-Content -Path $pfxPath -Value $encodedBytes -AsByteStream
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ SignedUninstaller=yes
|
|||
CloseApplications=no
|
||||
|
||||
[Files]
|
||||
Source: "bin\oh-my-posh.exe"; DestDir: "{app}\bin"; Flags: sign
|
||||
Source: "bin\oh-my-posh.exe"; DestDir: "{app}\bin"
|
||||
Source: "bin\themes\*"; DestDir: "{app}\themes"
|
||||
|
||||
[Registry]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Make sure to check the documentation at https://goreleaser.com
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
version: 2
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
|
@ -38,12 +39,18 @@ builds:
|
|||
goarch: arm
|
||||
hooks:
|
||||
post:
|
||||
- sh -c "cosign sign-blob --key $COSIGN_KEY_LOCATION {{ .Path }} > dist/{{ .Name }}.sig"
|
||||
- pwsh -c "if ('{{ .Path }}'.EndsWith('.exe')) { & '{{ .Env.SIGNTOOL }}' sign /f '{{ .Env.SIGNING_CERTIFICATE_LOCATION }}' /p '{{ .Env.SIGNING_CERTIFICATE_PASSWORD }}' /fd SHA256 /t http://timestamp.digicert.com '{{ .Path }}' }"
|
||||
archives:
|
||||
- id: oh-my-posh
|
||||
format: binary
|
||||
name_template: "posh-{{ .Os }}-{{ .Arch }}"
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
signs:
|
||||
- cmd: pwsh
|
||||
args:
|
||||
- "-c"
|
||||
- "& '{{ .Env.OPENSSL }}' pkeyutl -sign -inkey '{{ .Env.SHA_SIGNING_KEY_LOCATION }}' -out '${artifact}.sig' -rawin -in '${artifact}'"
|
||||
artifacts: checksum
|
||||
changelog:
|
||||
skip: true
|
||||
disable: true
|
||||
|
|
|
@ -1,673 +0,0 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/regex"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runewidth.DefaultCondition.EastAsianWidth = false
|
||||
}
|
||||
|
||||
var (
|
||||
knownStyles = []*style{
|
||||
{AnchorStart: `<b>`, AnchorEnd: `</b>`, Start: "\x1b[1m", End: "\x1b[22m"},
|
||||
{AnchorStart: `<u>`, AnchorEnd: `</u>`, Start: "\x1b[4m", End: "\x1b[24m"},
|
||||
{AnchorStart: `<o>`, AnchorEnd: `</o>`, Start: "\x1b[53m", End: "\x1b[55m"},
|
||||
{AnchorStart: `<i>`, AnchorEnd: `</i>`, Start: "\x1b[3m", End: "\x1b[23m"},
|
||||
{AnchorStart: `<s>`, AnchorEnd: `</s>`, Start: "\x1b[9m", End: "\x1b[29m"},
|
||||
{AnchorStart: `<d>`, AnchorEnd: `</d>`, Start: "\x1b[2m", End: "\x1b[22m"},
|
||||
{AnchorStart: `<f>`, AnchorEnd: `</f>`, Start: "\x1b[5m", End: "\x1b[25m"},
|
||||
{AnchorStart: `<r>`, AnchorEnd: `</r>`, Start: "\x1b[7m", End: "\x1b[27m"},
|
||||
}
|
||||
resetStyle = &style{AnchorStart: "RESET", AnchorEnd: `</>`, End: "\x1b[0m"}
|
||||
backgroundStyle = &style{AnchorStart: "BACKGROUND", AnchorEnd: `</>`, End: "\x1b[49m"}
|
||||
)
|
||||
|
||||
type style struct {
|
||||
AnchorStart string
|
||||
AnchorEnd string
|
||||
Start string
|
||||
End string
|
||||
}
|
||||
|
||||
type Colors struct {
|
||||
Background string `json:"background" toml:"background"`
|
||||
Foreground string `json:"foreground" toml:"foreground"`
|
||||
}
|
||||
|
||||
const (
|
||||
// Transparent implies a transparent color
|
||||
Transparent = "transparent"
|
||||
// Accent is the OS accent color
|
||||
Accent = "accent"
|
||||
// ParentBackground takes the previous segment's background color
|
||||
ParentBackground = "parentBackground"
|
||||
// ParentForeground takes the previous segment's color
|
||||
ParentForeground = "parentForeground"
|
||||
// Background takes the current segment's background color
|
||||
Background = "background"
|
||||
// Foreground takes the current segment's foreground color
|
||||
Foreground = "foreground"
|
||||
|
||||
AnchorRegex = `^(?P<ANCHOR><(?P<FG>[^,<>]+)?,?(?P<BG>[^<>]+)?>)`
|
||||
colorise = "\x1b[%sm"
|
||||
transparent = "\x1b[0m\x1b[%s;49m\x1b[7m"
|
||||
transparentEnd = "\x1b[27m"
|
||||
backgroundEnd = "\x1b[49m"
|
||||
|
||||
AnsiRegex = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
|
||||
|
||||
OSC99 = "osc99"
|
||||
OSC7 = "osc7"
|
||||
OSC51 = "osc51"
|
||||
|
||||
ANCHOR = "ANCHOR"
|
||||
BG = "BG"
|
||||
FG = "FG"
|
||||
|
||||
hyperLinkStart = "<LINK>"
|
||||
hyperLinkEnd = "</LINK>"
|
||||
hyperLinkText = "<TEXT>"
|
||||
hyperLinkTextEnd = "</TEXT>"
|
||||
)
|
||||
|
||||
// Writer writes colorized ANSI strings
|
||||
type Writer struct {
|
||||
TerminalBackground string
|
||||
Colors *Colors
|
||||
ParentColors []*Colors
|
||||
AnsiColors ColorString
|
||||
Plain bool
|
||||
TrueColor bool
|
||||
|
||||
builder strings.Builder
|
||||
length int
|
||||
|
||||
foreground Color
|
||||
background Color
|
||||
current ColorHistory
|
||||
runes []rune
|
||||
transparent bool
|
||||
invisible bool
|
||||
hyperlink bool
|
||||
|
||||
shell string
|
||||
format string
|
||||
left string
|
||||
title string
|
||||
linechange string
|
||||
clearBelow string
|
||||
clearLine string
|
||||
saveCursorPosition string
|
||||
restoreCursorPosition string
|
||||
escapeLeft string
|
||||
escapeRight string
|
||||
osc99 string
|
||||
osc7 string
|
||||
osc51 string
|
||||
|
||||
hyperlinkStart string
|
||||
hyperlinkCenter string
|
||||
hyperlinkEnd string
|
||||
|
||||
iTermPromptMark string
|
||||
iTermCurrentDir string
|
||||
iTermRemoteHost string
|
||||
}
|
||||
|
||||
func (w *Writer) Init(shellName string) {
|
||||
w.shell = shellName
|
||||
w.format = "%s"
|
||||
switch w.shell {
|
||||
case shell.BASH:
|
||||
w.format = "\\[%s\\]"
|
||||
w.linechange = "\\[\x1b[%d%s\\]"
|
||||
w.left = "\\[\x1b[%dD\\]"
|
||||
w.clearBelow = "\\[\x1b[0J\\]"
|
||||
w.clearLine = "\\[\x1b[K\\]"
|
||||
w.saveCursorPosition = "\\[\x1b7\\]"
|
||||
w.restoreCursorPosition = "\\[\x1b8\\]"
|
||||
w.title = "\\[\x1b]0;%s\007\\]"
|
||||
w.escapeLeft = "\\["
|
||||
w.escapeRight = "\\]"
|
||||
w.hyperlinkStart = "\\[\x1b]8;;"
|
||||
w.hyperlinkCenter = "\x1b\\\\\\]"
|
||||
w.hyperlinkEnd = "\\[\x1b]8;;\x1b\\\\\\]"
|
||||
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%%}"
|
||||
w.left = "%%{\x1b[%dD%%}"
|
||||
w.clearBelow = "%{\x1b[0J%}"
|
||||
w.clearLine = "%{\x1b[K%}"
|
||||
w.saveCursorPosition = "%{\x1b7%}"
|
||||
w.restoreCursorPosition = "%{\x1b8%}"
|
||||
w.title = "%%{\x1b]0;%s\007%%}"
|
||||
w.escapeLeft = "%{"
|
||||
w.escapeRight = "%}"
|
||||
w.hyperlinkStart = "%{\x1b]8;;"
|
||||
w.hyperlinkCenter = "\x1b\\%}"
|
||||
w.hyperlinkEnd = "%{\x1b]8;;\x1b\\%}"
|
||||
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"
|
||||
w.clearBelow = "\x1b[0J"
|
||||
w.clearLine = "\x1b[K"
|
||||
w.saveCursorPosition = "\x1b7"
|
||||
w.restoreCursorPosition = "\x1b8"
|
||||
w.title = "\x1b]0;%s\007"
|
||||
// when in fish on Linux, it seems hyperlinks ending with \\ print a \
|
||||
// unlike on macOS. However, this is a fish bug, so do not try to fix it here:
|
||||
// https://github.com/JanDeDobbeleer/oh-my-posh/pull/3288#issuecomment-1369137068
|
||||
w.hyperlinkStart = "\x1b]8;;"
|
||||
w.hyperlinkCenter = "\x1b\\"
|
||||
w.hyperlinkEnd = "\x1b]8;;\x1b\\"
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) SetColors(background, foreground string) {
|
||||
w.Colors = &Colors{
|
||||
Background: background,
|
||||
Foreground: foreground,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) SetParentColors(background, foreground string) {
|
||||
if w.ParentColors == nil {
|
||||
w.ParentColors = make([]*Colors, 0)
|
||||
}
|
||||
w.ParentColors = append([]*Colors{{
|
||||
Background: background,
|
||||
Foreground: foreground,
|
||||
}}, w.ParentColors...)
|
||||
}
|
||||
|
||||
func (w *Writer) ChangeLine(numberOfLines int) string {
|
||||
if w.Plain {
|
||||
return ""
|
||||
}
|
||||
position := "B"
|
||||
if numberOfLines < 0 {
|
||||
position = "F"
|
||||
numberOfLines = -numberOfLines
|
||||
}
|
||||
return fmt.Sprintf(w.linechange, numberOfLines, position)
|
||||
}
|
||||
|
||||
func (w *Writer) ConsolePwd(pwdType, userName, hostName, pwd string) string {
|
||||
if w.Plain {
|
||||
return ""
|
||||
}
|
||||
if strings.HasSuffix(pwd, ":") {
|
||||
pwd += "\\"
|
||||
}
|
||||
switch pwdType {
|
||||
case OSC7:
|
||||
return fmt.Sprintf(w.osc7, hostName, pwd)
|
||||
case OSC51:
|
||||
return fmt.Sprintf(w.osc51, userName, hostName, pwd)
|
||||
case OSC99:
|
||||
fallthrough
|
||||
default:
|
||||
return fmt.Sprintf(w.osc99, pwd)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) ClearAfter() string {
|
||||
if w.Plain {
|
||||
return ""
|
||||
}
|
||||
return w.clearLine + w.clearBelow
|
||||
}
|
||||
|
||||
func (w *Writer) FormatTitle(title string) string {
|
||||
title = w.trimAnsi(title)
|
||||
if w.Plain {
|
||||
return title
|
||||
}
|
||||
// we have to do this to prevent bash/zsh from misidentifying escape sequences
|
||||
switch w.shell {
|
||||
case shell.BASH:
|
||||
title = strings.NewReplacer("`", "\\`", `\`, `\\`).Replace(title)
|
||||
case shell.ZSH:
|
||||
title = strings.NewReplacer("`", "\\`", `%`, `%%`).Replace(title)
|
||||
case shell.ELVISH, shell.XONSH:
|
||||
// these shells don't support setting the title
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(w.title, title)
|
||||
}
|
||||
|
||||
func (w *Writer) FormatText(text string) string {
|
||||
return fmt.Sprintf(w.format, text)
|
||||
}
|
||||
|
||||
func (w *Writer) SaveCursorPosition() string {
|
||||
return w.saveCursorPosition
|
||||
}
|
||||
|
||||
func (w *Writer) RestoreCursorPosition() string {
|
||||
return w.restoreCursorPosition
|
||||
}
|
||||
|
||||
func (w *Writer) PromptStart() string {
|
||||
return fmt.Sprintf(w.format, "\x1b]133;A\007")
|
||||
}
|
||||
|
||||
func (w *Writer) CommandStart() string {
|
||||
return fmt.Sprintf(w.format, "\x1b]133;B\007")
|
||||
}
|
||||
|
||||
func (w *Writer) CommandFinished(code int, ignore bool) string {
|
||||
if ignore {
|
||||
return fmt.Sprintf(w.format, "\x1b]133;D\007")
|
||||
}
|
||||
mark := fmt.Sprintf("\x1b]133;D;%d\007", code)
|
||||
return fmt.Sprintf(w.format, mark)
|
||||
}
|
||||
|
||||
func (w *Writer) LineBreak() string {
|
||||
cr := fmt.Sprintf(w.left, 1000)
|
||||
lf := fmt.Sprintf(w.linechange, 1, "B")
|
||||
return cr + lf
|
||||
}
|
||||
|
||||
func (w *Writer) Write(background, foreground, text string) {
|
||||
if len(text) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
w.background, w.foreground = w.asAnsiColors(background, foreground)
|
||||
// default to white foreground
|
||||
if w.foreground.IsEmpty() {
|
||||
w.foreground = w.AnsiColors.ToColor("white", false, w.TrueColor)
|
||||
}
|
||||
// validate if we start with a color override
|
||||
match := regex.FindNamedRegexMatch(AnchorRegex, text)
|
||||
if len(match) != 0 && match[ANCHOR] != hyperLinkStart {
|
||||
colorOverride := true
|
||||
for _, style := range knownStyles {
|
||||
if match[ANCHOR] != style.AnchorStart {
|
||||
continue
|
||||
}
|
||||
w.writeEscapedAnsiString(style.Start)
|
||||
colorOverride = false
|
||||
}
|
||||
|
||||
if colorOverride {
|
||||
w.current.Add(w.asAnsiColors(match[BG], match[FG]))
|
||||
}
|
||||
}
|
||||
|
||||
w.writeSegmentColors()
|
||||
|
||||
// print the hyperlink part AFTER the coloring
|
||||
if match[ANCHOR] == hyperLinkStart {
|
||||
w.hyperlink = true
|
||||
w.builder.WriteString(w.hyperlinkStart)
|
||||
}
|
||||
|
||||
text = text[len(match[ANCHOR]):]
|
||||
w.runes = []rune(text)
|
||||
hyperlinkTextPosition := 0
|
||||
|
||||
for i := 0; i < len(w.runes); i++ {
|
||||
s := w.runes[i]
|
||||
// ignore everything which isn't overriding
|
||||
if s != '<' {
|
||||
w.write(s)
|
||||
continue
|
||||
}
|
||||
|
||||
// color/end overrides first
|
||||
text = string(w.runes[i:])
|
||||
match = regex.FindNamedRegexMatch(AnchorRegex, text)
|
||||
if len(match) > 0 {
|
||||
// check for hyperlinks first
|
||||
switch match[ANCHOR] {
|
||||
case hyperLinkStart:
|
||||
w.hyperlink = true
|
||||
i += len([]rune(match[ANCHOR])) - 1
|
||||
w.builder.WriteString(w.hyperlinkStart)
|
||||
continue
|
||||
case hyperLinkText:
|
||||
w.hyperlink = false
|
||||
i += len([]rune(match[ANCHOR])) - 1
|
||||
hyperlinkTextPosition = i
|
||||
w.builder.WriteString(w.hyperlinkCenter)
|
||||
continue
|
||||
case hyperLinkTextEnd:
|
||||
// this implies there's no text in the hyperlink
|
||||
if hyperlinkTextPosition+1 == i {
|
||||
w.builder.WriteString("link")
|
||||
w.length += 4
|
||||
}
|
||||
i += len([]rune(match[ANCHOR])) - 1
|
||||
continue
|
||||
case hyperLinkEnd:
|
||||
i += len([]rune(match[ANCHOR])) - 1
|
||||
w.builder.WriteString(w.hyperlinkEnd)
|
||||
continue
|
||||
}
|
||||
|
||||
i = w.writeArchorOverride(match, background, i)
|
||||
continue
|
||||
}
|
||||
|
||||
w.length += runewidth.RuneWidth(s)
|
||||
w.write(s)
|
||||
}
|
||||
|
||||
// reset colors
|
||||
w.writeEscapedAnsiString(resetStyle.End)
|
||||
|
||||
// pop last color from the stack
|
||||
w.current.Pop()
|
||||
}
|
||||
|
||||
func (w *Writer) String() (string, int) {
|
||||
defer func() {
|
||||
w.length = 0
|
||||
w.builder.Reset()
|
||||
}()
|
||||
|
||||
return w.builder.String(), w.length
|
||||
}
|
||||
|
||||
func (w *Writer) writeEscapedAnsiString(text string) {
|
||||
if w.Plain {
|
||||
return
|
||||
}
|
||||
|
||||
if len(w.format) != 0 {
|
||||
text = fmt.Sprintf(w.format, text)
|
||||
}
|
||||
|
||||
w.builder.WriteString(text)
|
||||
}
|
||||
|
||||
func (w *Writer) getAnsiFromColorString(colorString string, isBackground bool) Color {
|
||||
return w.AnsiColors.ToColor(colorString, isBackground, w.TrueColor)
|
||||
}
|
||||
|
||||
func (w *Writer) write(s rune) {
|
||||
if w.invisible {
|
||||
return
|
||||
}
|
||||
|
||||
if !w.hyperlink {
|
||||
w.length += runewidth.RuneWidth(s)
|
||||
}
|
||||
|
||||
w.builder.WriteRune(s)
|
||||
}
|
||||
|
||||
func (w *Writer) writeSegmentColors() {
|
||||
// use correct starting colors
|
||||
bg := w.background
|
||||
fg := w.foreground
|
||||
if !w.current.Background().IsEmpty() {
|
||||
bg = w.current.Background()
|
||||
}
|
||||
if !w.current.Foreground().IsEmpty() {
|
||||
fg = w.current.Foreground()
|
||||
}
|
||||
|
||||
// ignore processing fully tranparent colors
|
||||
w.invisible = fg.IsTransparent() && bg.IsTransparent()
|
||||
if w.invisible {
|
||||
return
|
||||
}
|
||||
|
||||
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()))
|
||||
} else if fg.IsTransparent() && !bg.IsEmpty() {
|
||||
w.transparent = true
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(transparent, bg))
|
||||
} else {
|
||||
if !bg.IsEmpty() && !bg.IsTransparent() {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, bg))
|
||||
}
|
||||
if !fg.IsEmpty() {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, fg))
|
||||
}
|
||||
}
|
||||
|
||||
// set current colors
|
||||
w.current.Add(bg, fg)
|
||||
}
|
||||
|
||||
func (w *Writer) writeArchorOverride(match map[string]string, background string, i int) int {
|
||||
position := i
|
||||
// check color reset first
|
||||
if match[ANCHOR] == resetStyle.AnchorEnd {
|
||||
return w.endColorOverride(position)
|
||||
}
|
||||
|
||||
position += len([]rune(match[ANCHOR])) - 1
|
||||
|
||||
for _, style := range knownStyles {
|
||||
if style.AnchorEnd == match[ANCHOR] {
|
||||
w.writeEscapedAnsiString(style.End)
|
||||
return position
|
||||
}
|
||||
if style.AnchorStart == match[ANCHOR] {
|
||||
w.writeEscapedAnsiString(style.Start)
|
||||
return position
|
||||
}
|
||||
}
|
||||
|
||||
if match[FG] == Transparent && len(match[BG]) == 0 {
|
||||
match[BG] = background
|
||||
}
|
||||
|
||||
bg, fg := w.asAnsiColors(match[BG], match[FG])
|
||||
|
||||
// ignore processing fully tranparent colors
|
||||
w.invisible = fg.IsTransparent() && bg.IsTransparent()
|
||||
if w.invisible {
|
||||
return position
|
||||
}
|
||||
|
||||
// make sure we have colors
|
||||
if fg.IsEmpty() {
|
||||
fg = w.foreground
|
||||
}
|
||||
if bg.IsEmpty() {
|
||||
bg = w.background
|
||||
}
|
||||
|
||||
w.current.Add(bg, fg)
|
||||
|
||||
if w.current.Foreground().IsTransparent() && len(w.TerminalBackground) != 0 {
|
||||
background := w.getAnsiFromColorString(w.TerminalBackground, false)
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, background))
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background().ToForeground()))
|
||||
return position
|
||||
}
|
||||
|
||||
if w.current.Foreground().IsTransparent() && !w.current.Background().IsTransparent() {
|
||||
w.transparent = true
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.current.Background()))
|
||||
return position
|
||||
}
|
||||
|
||||
if w.current.Background() != w.background {
|
||||
// end the colors in case we have a transparent background
|
||||
if w.current.Background().IsTransparent() {
|
||||
w.writeEscapedAnsiString(backgroundEnd)
|
||||
} else {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
|
||||
}
|
||||
}
|
||||
|
||||
if w.current.Foreground() != w.foreground {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
|
||||
}
|
||||
|
||||
return position
|
||||
}
|
||||
|
||||
func (w *Writer) endColorOverride(position int) int {
|
||||
// make sure to reset the colors if needed
|
||||
position += len([]rune(resetStyle.AnchorEnd)) - 1
|
||||
|
||||
// do not restore colors at the end of the string, we print it anyways
|
||||
if position == len(w.runes)-1 {
|
||||
w.current.Pop()
|
||||
return position
|
||||
}
|
||||
|
||||
// reset colors to previous when we have more than 1 in stack
|
||||
// as soon as we have more than 1, we can pop the last one
|
||||
// and print the previous override as it wasn't ended yet
|
||||
if w.current.Len() > 1 {
|
||||
fg := w.current.Foreground()
|
||||
bg := w.current.Background()
|
||||
|
||||
w.current.Pop()
|
||||
|
||||
previousBg := w.current.Background()
|
||||
previousFg := w.current.Foreground()
|
||||
|
||||
if w.transparent {
|
||||
w.writeEscapedAnsiString(transparentEnd)
|
||||
}
|
||||
|
||||
if previousBg != bg {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, previousBg))
|
||||
}
|
||||
|
||||
if previousFg != fg {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, previousFg))
|
||||
}
|
||||
|
||||
return position
|
||||
}
|
||||
|
||||
// pop the last colors from the stack
|
||||
defer w.current.Pop()
|
||||
|
||||
// do not reset when colors are identical
|
||||
if w.current.Background() == w.background && w.current.Foreground() == w.foreground {
|
||||
return position
|
||||
}
|
||||
|
||||
if w.transparent {
|
||||
w.writeEscapedAnsiString(transparentEnd)
|
||||
}
|
||||
|
||||
if w.background.IsClear() {
|
||||
w.writeEscapedAnsiString(backgroundStyle.End)
|
||||
}
|
||||
|
||||
if w.current.Background() != w.background && !w.background.IsClear() {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.background))
|
||||
}
|
||||
|
||||
if (w.current.Foreground() != w.foreground || w.transparent) && !w.foreground.IsClear() {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.foreground))
|
||||
}
|
||||
|
||||
w.transparent = false
|
||||
return position
|
||||
}
|
||||
|
||||
func (w *Writer) asAnsiColors(background, foreground string) (Color, Color) {
|
||||
if len(background) == 0 {
|
||||
background = Background
|
||||
}
|
||||
if len(foreground) == 0 {
|
||||
foreground = Foreground
|
||||
}
|
||||
background = w.expandKeyword(background)
|
||||
foreground = w.expandKeyword(foreground)
|
||||
inverted := foreground == Transparent && len(background) != 0
|
||||
backgroundAnsi := w.getAnsiFromColorString(background, !inverted)
|
||||
foregroundAnsi := w.getAnsiFromColorString(foreground, false)
|
||||
return backgroundAnsi, foregroundAnsi
|
||||
}
|
||||
|
||||
func (w *Writer) isKeyword(color string) bool {
|
||||
switch color {
|
||||
case Transparent, ParentBackground, ParentForeground, Background, Foreground:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) expandKeyword(keyword string) string {
|
||||
resolveParentColor := func(keyword string) string {
|
||||
for _, color := range w.ParentColors {
|
||||
if color == nil {
|
||||
return Transparent
|
||||
}
|
||||
switch keyword {
|
||||
case ParentBackground:
|
||||
keyword = color.Background
|
||||
case ParentForeground:
|
||||
keyword = color.Foreground
|
||||
default:
|
||||
if len(keyword) == 0 {
|
||||
return Transparent
|
||||
}
|
||||
return keyword
|
||||
}
|
||||
}
|
||||
if len(keyword) == 0 {
|
||||
return Transparent
|
||||
}
|
||||
return keyword
|
||||
}
|
||||
resolveKeyword := func(keyword string) string {
|
||||
switch {
|
||||
case keyword == Background && w.Colors != nil:
|
||||
return w.Colors.Background
|
||||
case keyword == Foreground && w.Colors != nil:
|
||||
return w.Colors.Foreground
|
||||
case (keyword == ParentBackground || keyword == ParentForeground) && w.ParentColors != nil:
|
||||
return resolveParentColor(keyword)
|
||||
default:
|
||||
return Transparent
|
||||
}
|
||||
}
|
||||
for ok := w.isKeyword(keyword); ok; ok = w.isKeyword(keyword) {
|
||||
resolved := resolveKeyword(keyword)
|
||||
if resolved == keyword {
|
||||
break
|
||||
}
|
||||
keyword = resolved
|
||||
}
|
||||
return keyword
|
||||
}
|
||||
|
||||
func (w *Writer) trimAnsi(text string) string {
|
||||
if len(text) == 0 || !strings.Contains(text, "\x1b") {
|
||||
return text
|
||||
}
|
||||
return regex.ReplaceAllString(AnsiRegex, text, "")
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package ansi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
)
|
||||
|
||||
func TestGetAnsiFromColorString(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Expected Color
|
||||
Color string
|
||||
Background bool
|
||||
Color256 bool
|
||||
}{
|
||||
{Case: "256 color", Expected: Color("38;5;99"), Color: "99", Background: false},
|
||||
{Case: "256 color", Expected: Color("38;5;122"), Color: "122", Background: false},
|
||||
{Case: "Invalid background", Expected: emptyColor, Color: "invalid", Background: true},
|
||||
{Case: "Invalid background", Expected: emptyColor, Color: "invalid", Background: false},
|
||||
{Case: "Hex foreground", Expected: Color("38;2;170;187;204"), Color: "#AABBCC", Background: false},
|
||||
{Case: "Hex backgrond", Expected: Color("48;2;170;187;204"), Color: "#AABBCC", Background: true},
|
||||
{Case: "Base 8 foreground", Expected: Color("31"), Color: "red", Background: false},
|
||||
{Case: "Base 8 background", Expected: Color("41"), Color: "red", Background: true},
|
||||
{Case: "Base 16 foreground", Expected: Color("91"), Color: "lightRed", Background: false},
|
||||
{Case: "Base 16 backround", Expected: Color("101"), Color: "lightRed", Background: true},
|
||||
{Case: "Non true color TERM", Expected: Color("38;5;146"), Color: "#AABBCC", Color256: true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
ansiColors := &DefaultColors{}
|
||||
ansiColor := ansiColors.ToColor(tc.Color, tc.Background, !tc.Color256)
|
||||
assert.Equal(t, tc.Expected, ansiColor, tc.Case)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeColors(t *testing.T) {
|
||||
env := &mock.MockedEnvironment{}
|
||||
env.On("Flags").Return(&platform.Flags{
|
||||
TrueColor: true,
|
||||
})
|
||||
env.On("WindowsRegistryKeyValue", `HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`).Return(&platform.WindowsRegistryValue{}, errors.New("err"))
|
||||
colors := MakeColors(nil, false, "", env)
|
||||
assert.IsType(t, &DefaultColors{}, colors)
|
||||
|
||||
colors = MakeColors(nil, true, "", env)
|
||||
assert.IsType(t, &CachedColors{}, colors)
|
||||
assert.IsType(t, &DefaultColors{}, colors.(*CachedColors).ansiColors)
|
||||
|
||||
colors = MakeColors(testPalette, false, "", env)
|
||||
assert.IsType(t, &PaletteColors{}, colors)
|
||||
assert.IsType(t, &DefaultColors{}, colors.(*PaletteColors).ansiColors)
|
||||
|
||||
colors = MakeColors(testPalette, true, "", env)
|
||||
assert.IsType(t, &CachedColors{}, colors)
|
||||
assert.IsType(t, &PaletteColors{}, colors.(*CachedColors).ansiColors)
|
||||
assert.IsType(t, &DefaultColors{}, colors.(*CachedColors).ansiColors.(*PaletteColors).ansiColors)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package ansi
|
||||
|
||||
import "github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
||||
func GetAccentColor(_ platform.Environment) (*RGB, error) {
|
||||
return nil, &platform.NotImplemented{}
|
||||
}
|
||||
|
||||
func (d *DefaultColors) SetAccentColor(env platform.Environment, defaultColor string) {
|
||||
if len(defaultColor) == 0 {
|
||||
return
|
||||
}
|
||||
d.accent = &Colors{
|
||||
Foreground: string(d.ToColor(defaultColor, false, env.Flags().TrueColor)),
|
||||
Background: string(d.ToColor(defaultColor, true, env.Flags().TrueColor)),
|
||||
}
|
||||
}
|
65
src/cache/cache.go
vendored
Normal file
65
src/cache/cache.go
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||
)
|
||||
|
||||
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 (
|
||||
FileName = "omp.cache"
|
||||
)
|
||||
|
||||
var SessionFileName = fmt.Sprintf("%s.%s", FileName, pid())
|
||||
|
||||
func pid() string {
|
||||
pid := os.Getenv("POSH_PID")
|
||||
if len(pid) == 0 {
|
||||
log.Debug("POSH_PID not set, using process pid")
|
||||
pid = strconv.Itoa(os.Getppid())
|
||||
}
|
||||
|
||||
return pid
|
||||
}
|
||||
|
||||
const (
|
||||
TEMPLATECACHE = "template_cache"
|
||||
TOGGLECACHE = "toggle_cache"
|
||||
PROMPTCOUNTCACHE = "prompt_count_cache"
|
||||
ENGINECACHE = "engine_cache"
|
||||
FONTLISTCACHE = "font_list_cache"
|
||||
|
||||
ONEDAY = 1440
|
||||
ONEWEEK = 10080
|
||||
ONEMONTH = 43200
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Value string `json:"value"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
TTL int `json:"ttl"`
|
||||
}
|
||||
|
||||
func (c *Entry) Expired() bool {
|
||||
if c.TTL < 0 {
|
||||
return false
|
||||
}
|
||||
return time.Now().Unix() >= (c.Timestamp + int64(c.TTL)*60)
|
||||
}
|
22
src/cache/command.go
vendored
Normal file
22
src/cache/command.go
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/maps"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
Commands *maps.Concurrent
|
||||
}
|
||||
|
||||
func (c *Command) Set(command, path string) {
|
||||
c.Commands.Set(command, path)
|
||||
}
|
||||
|
||||
func (c *Command) Get(command string) (string, bool) {
|
||||
cacheCommand, found := c.Commands.Get(command)
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
command, ok := cacheCommand.(string)
|
||||
return command, ok
|
||||
}
|
88
src/cache/file.go
vendored
Normal file
88
src/cache/file.go
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/log"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/maps"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
cache *maps.Concurrent
|
||||
cacheFilePath string
|
||||
dirty bool
|
||||
}
|
||||
|
||||
func (fc *File) Init(cacheFilePath string) {
|
||||
defer log.Trace(time.Now(), cacheFilePath)
|
||||
|
||||
fc.cache = maps.NewConcurrent()
|
||||
fc.cacheFilePath = cacheFilePath
|
||||
|
||||
log.Debug("loading cache file:", fc.cacheFilePath)
|
||||
|
||||
content, err := os.ReadFile(fc.cacheFilePath)
|
||||
if err != nil {
|
||||
// set to dirty so we create it on close
|
||||
fc.dirty = true
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var list map[string]*Entry
|
||||
if err := json.Unmarshal(content, &list); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for key, co := range list {
|
||||
if co.Expired() {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("loading cache key:", key)
|
||||
fc.cache.Set(key, co)
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *File) Close() {
|
||||
if !fc.dirty {
|
||||
return
|
||||
}
|
||||
|
||||
cache := fc.cache.ToSimple()
|
||||
|
||||
if dump, err := json.MarshalIndent(cache, "", " "); err == nil {
|
||||
_ = os.WriteFile(fc.cacheFilePath, dump, 0644)
|
||||
}
|
||||
}
|
||||
|
||||
// returns the value for the given key as long as
|
||||
// the TTL (minutes) is not expired
|
||||
func (fc *File) Get(key string) (string, bool) {
|
||||
val, found := fc.cache.Get(key)
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
if co, ok := val.(*Entry); ok {
|
||||
return co.Value, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// sets the value for the given key with a TTL (minutes)
|
||||
func (fc *File) Set(key, value string, ttl int) {
|
||||
fc.cache.Set(key, &Entry{
|
||||
Value: value,
|
||||
Timestamp: time.Now().Unix(),
|
||||
TTL: ttl,
|
||||
})
|
||||
fc.dirty = true
|
||||
}
|
||||
|
||||
// delete the key from the cache
|
||||
func (fc *File) Delete(key string) {
|
||||
fc.cache.Delete(key)
|
||||
fc.dirty = true
|
||||
}
|
14
src/mock/cache.go → src/cache/mock/cache.go
vendored
14
src/mock/cache.go → src/cache/mock/cache.go
vendored
|
@ -2,18 +2,18 @@ package mock
|
|||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// MockedCache is an autogenerated mock type for the cache type
|
||||
type MockedCache struct {
|
||||
// Cache is an autogenerated mock type for the cache type
|
||||
type Cache struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// close provides a mock function with given fields:
|
||||
func (_m *MockedCache) Close() {
|
||||
func (_m *Cache) Close() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// get provides a mock function with given fields: key
|
||||
func (_m *MockedCache) Get(key string) (string, bool) {
|
||||
func (_m *Cache) Get(key string) (string, bool) {
|
||||
ret := _m.Called(key)
|
||||
|
||||
var r0 string
|
||||
|
@ -34,16 +34,16 @@ func (_m *MockedCache) Get(key string) (string, bool) {
|
|||
}
|
||||
|
||||
// init provides a mock function with given fields: home
|
||||
func (_m *MockedCache) Init(home string) {
|
||||
func (_m *Cache) Init(home string) {
|
||||
_m.Called(home)
|
||||
}
|
||||
|
||||
// set provides a mock function with given fields: key, value, ttl
|
||||
func (_m *MockedCache) Set(key, value string, ttl int) {
|
||||
func (_m *Cache) Set(key, value string, ttl int) {
|
||||
_m.Called(key, value, ttl)
|
||||
}
|
||||
|
||||
// delete provides a mock function with given fields: key
|
||||
func (_m *MockedCache) Delete(key string) {
|
||||
func (_m *Cache) Delete(key string) {
|
||||
_m.Called(key)
|
||||
}
|
40
src/cache/template.go
vendored
Normal file
40
src/cache/template.go
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/maps"
|
||||
)
|
||||
|
||||
type Template struct {
|
||||
Root bool
|
||||
PWD string
|
||||
AbsolutePWD string
|
||||
PSWD string
|
||||
Folder string
|
||||
Shell string
|
||||
ShellVersion string
|
||||
UserName string
|
||||
HostName string
|
||||
Code int
|
||||
Env map[string]string
|
||||
Var maps.Simple
|
||||
OS string
|
||||
WSL bool
|
||||
PromptCount int
|
||||
SHLVL int
|
||||
Jobs int
|
||||
Segments *maps.Concurrent
|
||||
SegmentsCache maps.Simple
|
||||
|
||||
Initialized bool
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (t *Template) AddSegmentData(key string, value any) {
|
||||
t.Segments.Set(key, value)
|
||||
}
|
||||
|
||||
func (t *Template) RemoveSegmentData(key string) {
|
||||
t.Segments.Delete(key)
|
||||
}
|
|
@ -7,7 +7,8 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -34,24 +35,21 @@ You can do the following:
|
|||
_ = cmd.Help()
|
||||
return
|
||||
}
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{},
|
||||
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{},
|
||||
}
|
||||
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
switch args[0] {
|
||||
case "path":
|
||||
fmt.Print(env.CachePath())
|
||||
fmt.Println(env.CachePath())
|
||||
case "clear":
|
||||
cacheFilePath := filepath.Join(env.CachePath(), platform.CacheFile)
|
||||
err := os.Remove(cacheFilePath)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Printf("removed cache file at %s\n", cacheFilePath)
|
||||
clear(env.CachePath())
|
||||
case "edit":
|
||||
cacheFilePath := filepath.Join(env.CachePath(), platform.CacheFile)
|
||||
cacheFilePath := filepath.Join(env.CachePath(), cache.FileName)
|
||||
editFileWithEditor(cacheFilePath)
|
||||
}
|
||||
},
|
||||
|
@ -63,16 +61,42 @@ func init() {
|
|||
|
||||
func editFileWithEditor(file string) {
|
||||
editor := os.Getenv("EDITOR")
|
||||
|
||||
var args []string
|
||||
if strings.Contains(editor, " ") {
|
||||
splitted := strings.Split(editor, " ")
|
||||
editor = splitted[0]
|
||||
args = splitted[1:]
|
||||
}
|
||||
|
||||
args = append(args, file)
|
||||
cmd := exec.Command(editor, args...)
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func clear(cachePath string) {
|
||||
// get all files in the cache directory that start with omp.cache and delete them
|
||||
files, err := os.ReadDir(cachePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(file.Name(), cache.FileName) {
|
||||
continue
|
||||
}
|
||||
|
||||
path := filepath.Join(cachePath, file.Name())
|
||||
if err := os.Remove(path); err == nil {
|
||||
fmt.Println("removed cache file:", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -36,14 +36,14 @@ Exports the ~/myconfig.omp.json config file and prints the result to stdout.
|
|||
Exports the ~/myconfig.omp.json config file to toml and prints the result to stdout.`,
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Config: configFlag,
|
||||
},
|
||||
}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
if len(output) == 0 && len(format) == 0 {
|
||||
// usage error
|
||||
|
@ -69,11 +69,11 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
|
|||
|
||||
switch format {
|
||||
case "json", "jsonc":
|
||||
format = engine.JSON
|
||||
format = config.JSON
|
||||
case "toml", "tml":
|
||||
format = engine.TOML
|
||||
format = config.TOML
|
||||
case "yaml", "yml":
|
||||
format = engine.YAML
|
||||
format = config.YAML
|
||||
default:
|
||||
// data error
|
||||
os.Exit(65)
|
||||
|
@ -83,16 +83,18 @@ Exports the ~/myconfig.omp.json config file to toml and prints the result to std
|
|||
},
|
||||
}
|
||||
|
||||
func cleanOutputPath(path string, env platform.Environment) string {
|
||||
func cleanOutputPath(path string, env runtime.Environment) string {
|
||||
if strings.HasPrefix(path, "~") {
|
||||
path = strings.TrimPrefix(path, "~")
|
||||
path = filepath.Join(env.Home(), path)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
if absPath, err := filepath.Abs(path); err == nil {
|
||||
path = absPath
|
||||
}
|
||||
}
|
||||
|
||||
return filepath.Clean(path)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,12 @@ 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/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/image"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/prompt"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -47,9 +49,9 @@ Exports the config to an image file ~/mytheme.png.
|
|||
Exports the config to an image file using customized output options.`,
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Config: configFlag,
|
||||
Shell: shell.GENERIC,
|
||||
TerminalWidth: 150,
|
||||
},
|
||||
|
@ -57,7 +59,7 @@ Exports the config to an image file using customized output options.`,
|
|||
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
// set sane defaults for things we don't print
|
||||
cfg.ConsoleTitleTemplate = ""
|
||||
|
@ -67,26 +69,21 @@ Exports the config to an image file using customized output options.`,
|
|||
// add variables to the environment
|
||||
env.Var = cfg.Var
|
||||
|
||||
writerColors := cfg.MakeColors()
|
||||
writer := &ansi.Writer{
|
||||
TerminalBackground: shell.ConsoleBackgroundColor(env, cfg.TerminalBackground),
|
||||
AnsiColors: writerColors,
|
||||
TrueColor: env.CmdFlags.TrueColor,
|
||||
}
|
||||
writer.Init(shell.GENERIC)
|
||||
eng := &engine.Engine{
|
||||
terminal.Init(shell.GENERIC)
|
||||
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
|
||||
terminal.Colors = cfg.MakeColors()
|
||||
|
||||
eng := &prompt.Engine{
|
||||
Config: cfg,
|
||||
Env: env,
|
||||
Writer: writer,
|
||||
}
|
||||
|
||||
prompt := eng.Primary()
|
||||
primaryPrompt := eng.Primary()
|
||||
|
||||
imageCreator := &engine.ImageRenderer{
|
||||
AnsiString: prompt,
|
||||
imageCreator := &image.Renderer{
|
||||
AnsiString: primaryPrompt,
|
||||
Author: author,
|
||||
BgColor: bgColor,
|
||||
Ansi: writer,
|
||||
}
|
||||
|
||||
if outputImage != "" {
|
||||
|
|
|
@ -3,8 +3,8 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -39,15 +39,15 @@ Migrates the ~/myconfig.omp.json config file to TOML and writes the result to yo
|
|||
A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Config: configFlag,
|
||||
Migrate: true,
|
||||
},
|
||||
}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
if write {
|
||||
cfg.BackupAndMigrate()
|
||||
return
|
||||
|
|
|
@ -3,8 +3,8 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -34,15 +34,15 @@ Migrates the ~/myconfig.omp.json config file's glyphs and writes the result to y
|
|||
A backup of the current config can be found at ~/myconfig.omp.json.bak.`,
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Config: configFlag,
|
||||
},
|
||||
}
|
||||
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
cfg.MigrateGlyphs = true
|
||||
if len(format) == 0 {
|
||||
|
|
|
@ -4,30 +4,37 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/build"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/prompt"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// debugCmd represents the prompt command
|
||||
var debugCmd = &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Print the prompt in debug mode",
|
||||
Long: "Print the prompt in debug mode.",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
Use: "debug [bash|zsh|fish|powershell|pwsh|cmd|nu|tcsh|elvish|xonsh]",
|
||||
Short: "Print the prompt in debug mode",
|
||||
Long: "Print the prompt in debug mode.",
|
||||
ValidArgs: supportedShells,
|
||||
Args: NoArgsOrOneValidArg,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
startTime := time.Now()
|
||||
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
if len(args) == 0 {
|
||||
_ = cmd.Help()
|
||||
return
|
||||
}
|
||||
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Config: configFlag,
|
||||
Debug: true,
|
||||
PWD: pwd,
|
||||
Shell: shellName,
|
||||
Shell: args[0],
|
||||
Plain: plain,
|
||||
},
|
||||
}
|
||||
|
@ -35,24 +42,19 @@ var debugCmd = &cobra.Command{
|
|||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(env)
|
||||
|
||||
// add variables to the environment
|
||||
env.Var = cfg.Var
|
||||
|
||||
writerColors := cfg.MakeColors()
|
||||
writer := &ansi.Writer{
|
||||
TerminalBackground: shell.ConsoleBackgroundColor(env, cfg.TerminalBackground),
|
||||
AnsiColors: writerColors,
|
||||
Plain: plain,
|
||||
TrueColor: env.CmdFlags.TrueColor,
|
||||
}
|
||||
terminal.Init(shell.GENERIC)
|
||||
terminal.BackgroundColor = cfg.TerminalBackground.ResolveTemplate(env)
|
||||
terminal.Colors = cfg.MakeColors()
|
||||
terminal.Plain = plain
|
||||
|
||||
writer.Init(shell.GENERIC)
|
||||
eng := &engine.Engine{
|
||||
eng := &prompt.Engine{
|
||||
Config: cfg,
|
||||
Env: env,
|
||||
Writer: writer,
|
||||
Plain: plain,
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -42,8 +42,8 @@ func init() {
|
|||
}
|
||||
|
||||
func toggleFeature(cmd *cobra.Command, feature string, enable bool) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Shell: shellName,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,14 +4,13 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/font"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
user bool
|
||||
|
||||
// fontCmd can work with fonts
|
||||
fontCmd = &cobra.Command{
|
||||
Use: "font [install|configure]",
|
||||
|
@ -36,25 +35,15 @@ This command is used to install fonts and configure the font in your terminal.
|
|||
if len(args) > 1 {
|
||||
fontName = args[1]
|
||||
}
|
||||
env := &platform.Shell{}
|
||||
|
||||
env := &runtime.Terminal{}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
// Windows users need to specify the --user flag if they want to install the font as user
|
||||
// If the user does not specify the --user flag, the font will be installed as a system font
|
||||
// and therefore we need to be administrator
|
||||
system := env.Root()
|
||||
if env.GOOS() == platform.WINDOWS && !user && !system {
|
||||
fmt.Println(`
|
||||
You need to be administrator to install a font as system font.
|
||||
You can either run this command as administrator or specify the --user flag to install the font for your user only:
|
||||
terminal.Init(env.Shell())
|
||||
|
||||
oh-my-posh font install --user
|
||||
`)
|
||||
return
|
||||
}
|
||||
font.Run(fontName, env)
|
||||
|
||||
font.Run(fontName, system)
|
||||
return
|
||||
case "configure":
|
||||
fmt.Println("not implemented")
|
||||
|
@ -67,5 +56,4 @@ This command is used to install fonts and configure the font in your terminal.
|
|||
|
||||
func init() {
|
||||
RootCmd.AddCommand(fontCmd)
|
||||
fontCmd.Flags().BoolVar(&user, "user", false, "install font as user")
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
color2 "github.com/gookit/color"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -44,8 +45,8 @@ This command is used to get the value of the following variables:
|
|||
return
|
||||
}
|
||||
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Shell: shellName,
|
||||
},
|
||||
}
|
||||
|
@ -56,7 +57,7 @@ This command is used to get the value of the following variables:
|
|||
case "shell":
|
||||
fmt.Println(env.Shell())
|
||||
case "accent":
|
||||
rgb, err := ansi.GetAccentColor(env)
|
||||
rgb, err := color.GetAccentColor(env)
|
||||
if err != nil {
|
||||
fmt.Println("error getting accent color:", err.Error())
|
||||
return
|
||||
|
@ -64,8 +65,7 @@ This command is used to get the value of the following variables:
|
|||
accent := color2.RGB(rgb.R, rgb.G, rgb.B)
|
||||
fmt.Println("#" + accent.Hex())
|
||||
case "toggles":
|
||||
cache := env.Cache()
|
||||
togglesCache, _ := cache.Get(platform.TOGGLECACHE)
|
||||
togglesCache, _ := env.Session().Get(cache.TOGGLECACHE)
|
||||
var toggles []string
|
||||
if len(togglesCache) != 0 {
|
||||
toggles = strings.Split(togglesCache, ",")
|
||||
|
|
|
@ -2,12 +2,11 @@ package cli
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"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/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -16,6 +15,20 @@ var (
|
|||
printOutput bool
|
||||
strict bool
|
||||
manual bool
|
||||
debug bool
|
||||
|
||||
supportedShells = []string{
|
||||
"bash",
|
||||
"zsh",
|
||||
"fish",
|
||||
"powershell",
|
||||
"pwsh",
|
||||
"cmd",
|
||||
"nu",
|
||||
"tcsh",
|
||||
"elvish",
|
||||
"xonsh",
|
||||
}
|
||||
|
||||
initCmd = &cobra.Command{
|
||||
Use: "init [bash|zsh|fish|powershell|pwsh|cmd|nu|tcsh|elvish|xonsh]",
|
||||
|
@ -23,19 +36,8 @@ var (
|
|||
Long: `Initialize your shell and config.
|
||||
|
||||
See the documentation to initialize your shell: https://ohmyposh.dev/docs/installation/prompt.`,
|
||||
ValidArgs: []string{
|
||||
"bash",
|
||||
"zsh",
|
||||
"fish",
|
||||
"powershell",
|
||||
"pwsh",
|
||||
"cmd",
|
||||
"nu",
|
||||
"tcsh",
|
||||
"elvish",
|
||||
"xonsh",
|
||||
},
|
||||
Args: NoArgsOrOneValidArg,
|
||||
ValidArgs: supportedShells,
|
||||
Args: NoArgsOrOneValidArg,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
_ = cmd.Help()
|
||||
|
@ -50,51 +52,40 @@ 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")
|
||||
initCmd.Flags().BoolVar(&debug, "debug", false, "enable/disable debug mode")
|
||||
_ = initCmd.MarkPersistentFlagRequired("config")
|
||||
RootCmd.AddCommand(initCmd)
|
||||
}
|
||||
|
||||
func runInit(shellName string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
var startTime time.Time
|
||||
if debug {
|
||||
startTime = time.Now()
|
||||
}
|
||||
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{
|
||||
Shell: shellName,
|
||||
Config: config,
|
||||
Config: configFlag,
|
||||
Strict: strict,
|
||||
Manual: manual,
|
||||
Debug: debug,
|
||||
},
|
||||
}
|
||||
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
cfg := engine.LoadConfig(env)
|
||||
cfg := config.Load(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)
|
||||
feats := cfg.Features()
|
||||
|
||||
for i, block := range cfg.Blocks {
|
||||
// only fetch cursor position when relevant
|
||||
if !cfg.DisableCursorPositioning && (i == 0 && block.Newline) {
|
||||
shell.CursorPositioning = true
|
||||
}
|
||||
if block.Type == engine.RPrompt {
|
||||
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)
|
||||
if printOutput || debug {
|
||||
init := shell.PrintInit(env, feats, &startTime)
|
||||
fmt.Print(init)
|
||||
return
|
||||
}
|
||||
|
||||
init := shell.Init(env)
|
||||
init := shell.Init(env, feats)
|
||||
fmt.Print(init)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -15,13 +15,13 @@ var noticeCmd = &cobra.Command{
|
|||
Long: "Print the upgrade notice when a new version is available.",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{},
|
||||
env := &runtime.Terminal{
|
||||
CmdFlags: &runtime.Flags{},
|
||||
}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
if notice, hasNotice := upgrade.Notice(env); hasNotice {
|
||||
if notice, hasNotice := upgrade.Notice(env, false); hasNotice {
|
||||
fmt.Println(notice)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3,8 +3,8 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/engine"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/prompt"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -19,6 +19,8 @@ var (
|
|||
terminalWidth int
|
||||
eval bool
|
||||
cleared bool
|
||||
cached bool
|
||||
jobCount int
|
||||
|
||||
command string
|
||||
shellVersion string
|
||||
|
@ -49,8 +51,8 @@ var printCmd = &cobra.Command{
|
|||
return
|
||||
}
|
||||
|
||||
flags := &platform.Flags{
|
||||
Config: config,
|
||||
flags := &runtime.Flags{
|
||||
Config: configFlag,
|
||||
PWD: pwd,
|
||||
PSWD: pswd,
|
||||
ErrorCode: status,
|
||||
|
@ -64,30 +66,32 @@ var printCmd = &cobra.Command{
|
|||
Plain: plain,
|
||||
Primary: args[0] == "primary",
|
||||
Cleared: cleared,
|
||||
Cached: cached,
|
||||
NoExitCode: noStatus,
|
||||
Column: column,
|
||||
JobCount: jobCount,
|
||||
}
|
||||
|
||||
eng := engine.New(flags)
|
||||
eng := prompt.New(flags)
|
||||
defer eng.Env.Close()
|
||||
|
||||
switch args[0] {
|
||||
case "debug":
|
||||
fmt.Print(eng.ExtraPrompt(engine.Debug))
|
||||
fmt.Print(eng.ExtraPrompt(prompt.Debug))
|
||||
case "primary":
|
||||
fmt.Print(eng.Primary())
|
||||
case "secondary":
|
||||
fmt.Print(eng.ExtraPrompt(engine.Secondary))
|
||||
fmt.Print(eng.ExtraPrompt(prompt.Secondary))
|
||||
case "transient":
|
||||
fmt.Print(eng.ExtraPrompt(engine.Transient))
|
||||
fmt.Print(eng.ExtraPrompt(prompt.Transient))
|
||||
case "right":
|
||||
fmt.Print(eng.RPrompt())
|
||||
case "tooltip":
|
||||
fmt.Print(eng.Tooltip(command))
|
||||
case "valid":
|
||||
fmt.Print(eng.ExtraPrompt(engine.Valid))
|
||||
fmt.Print(eng.ExtraPrompt(prompt.Valid))
|
||||
case "error":
|
||||
fmt.Print(eng.ExtraPrompt(engine.Error))
|
||||
fmt.Print(eng.ExtraPrompt(prompt.Error))
|
||||
default:
|
||||
_ = cmd.Help()
|
||||
}
|
||||
|
@ -110,8 +114,12 @@ func init() {
|
|||
printCmd.Flags().BoolVar(&cleared, "cleared", false, "do we have a clear terminal or not")
|
||||
printCmd.Flags().BoolVar(&eval, "eval", false, "output the prompt for eval")
|
||||
printCmd.Flags().IntVar(&column, "column", 0, "the column position of the cursor")
|
||||
// Deprecated flags
|
||||
printCmd.Flags().IntVar(&jobCount, "job-count", 0, "number of background jobs")
|
||||
|
||||
// Deprecated flags, keep to not break CLI integration
|
||||
printCmd.Flags().IntVarP(&status, "error", "e", 0, "last exit code")
|
||||
printCmd.Flags().BoolVar(&noStatus, "no-exit-code", false, "no valid exit code (cancelled or no command yet)")
|
||||
printCmd.Flags().BoolVar(&cached, "cached", false, "use a cached prompt")
|
||||
|
||||
RootCmd.AddCommand(printCmd)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
config string
|
||||
configFlag string
|
||||
displayVersion bool
|
||||
)
|
||||
|
||||
|
@ -48,7 +48,7 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
RootCmd.PersistentFlags().StringVarP(&config, "config", "c", "", "config file path")
|
||||
RootCmd.PersistentFlags().StringVarP(&configFlag, "config", "c", "", "config file path")
|
||||
RootCmd.Flags().BoolVarP(&initialize, "init", "i", false, "init (deprecated)")
|
||||
RootCmd.Flags().BoolVar(&displayVersion, "version", false, "version")
|
||||
RootCmd.Flags().StringVarP(&shellName, "shell", "s", "", "shell (deprecated)")
|
||||
|
|
|
@ -3,8 +3,8 @@ package cli
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -19,12 +19,11 @@ var toggleCmd = &cobra.Command{
|
|||
_ = cmd.Help()
|
||||
return
|
||||
}
|
||||
env := &platform.Shell{}
|
||||
env := &runtime.Terminal{}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
cache := env.Cache()
|
||||
togglesCache, _ := cache.Get(platform.TOGGLECACHE)
|
||||
togglesCache, _ := env.Session().Get(cache.TOGGLECACHE)
|
||||
var toggles []string
|
||||
if len(togglesCache) != 0 {
|
||||
toggles = strings.Split(togglesCache, ",")
|
||||
|
@ -45,7 +44,7 @@ var toggleCmd = &cobra.Command{
|
|||
newToggles = append(newToggles, segment)
|
||||
}
|
||||
|
||||
cache.Set(platform.TOGGLECACHE, strings.Join(newToggles, ","), 1440)
|
||||
env.Session().Set(cache.TOGGLECACHE, strings.Join(newToggles, ","), 1440)
|
||||
},
|
||||
}
|
||||
|
||||
|
|
95
src/cli/upgrade.go
Normal file
95
src/cli/upgrade.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
stdruntime "runtime"
|
||||
"slices"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/build"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/config"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/upgrade"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var force bool
|
||||
|
||||
// noticeCmd represents the get command
|
||||
var upgradeCmd = &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Short: "Upgrade when a new version is available.",
|
||||
Long: "Upgrade when a new version is available.",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
supportedPlatforms := []string{
|
||||
runtime.WINDOWS,
|
||||
runtime.DARWIN,
|
||||
runtime.LINUX,
|
||||
}
|
||||
|
||||
if !slices.Contains(supportedPlatforms, stdruntime.GOOS) {
|
||||
fmt.Print("\n⚠️ upgrade is not supported on this platform\n\n")
|
||||
return
|
||||
}
|
||||
|
||||
env := &runtime.Terminal{}
|
||||
env.Init()
|
||||
defer env.Close()
|
||||
|
||||
terminal.Init(env.Shell())
|
||||
fmt.Print(terminal.StartProgress())
|
||||
|
||||
latest, err := upgrade.Latest(env)
|
||||
if err != nil {
|
||||
fmt.Printf("\n❌ %s\n\n%s", err, terminal.StopProgress())
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
if force {
|
||||
executeUpgrade(latest)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Load(env)
|
||||
|
||||
version := fmt.Sprintf("v%s", build.Version)
|
||||
|
||||
if upgrade.IsMajorUpgrade(version, latest) {
|
||||
message := terminal.StopProgress()
|
||||
message += fmt.Sprintf("\n🚨 major upgrade available: %s -> %s, use oh-my-posh upgrade --force to upgrade\n\n", version, latest)
|
||||
fmt.Print(message)
|
||||
return
|
||||
}
|
||||
|
||||
if version != latest {
|
||||
executeUpgrade(latest)
|
||||
return
|
||||
}
|
||||
|
||||
message := terminal.StopProgress()
|
||||
|
||||
if !cfg.DisableNotice {
|
||||
message += "\n✅ no new version available\n\n"
|
||||
}
|
||||
|
||||
fmt.Print(message)
|
||||
},
|
||||
}
|
||||
|
||||
func executeUpgrade(latest string) {
|
||||
err := upgrade.Run(latest)
|
||||
fmt.Print(terminal.StopProgress())
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func init() {
|
||||
upgradeCmd.Flags().BoolVarP(&force, "force", "f", false, "force the upgrade even if the version is up to date")
|
||||
RootCmd.AddCommand(upgradeCmd)
|
||||
}
|
|
@ -1,37 +1,39 @@
|
|||
package ansi
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
)
|
||||
|
||||
// ColorString is the interface that wraps ToColor method.
|
||||
var TrueColor = true
|
||||
|
||||
// String is the interface that wraps ToColor method.
|
||||
//
|
||||
// ToColor gets the ANSI color code for a given color string.
|
||||
// This can include a valid hex color in the format `#FFFFFF`,
|
||||
// but also a name of one of the first 16 ANSI colors like `lightBlue`.
|
||||
type ColorString interface {
|
||||
ToColor(colorString string, isBackground bool, trueColor bool) Color
|
||||
type String interface {
|
||||
ToAnsi(colorString Ansi, isBackground bool) Ansi
|
||||
}
|
||||
|
||||
type ColorSet struct {
|
||||
Foreground Color
|
||||
Background Color
|
||||
type Set struct {
|
||||
Background Ansi `json:"background" toml:"background"`
|
||||
Foreground Ansi `json:"foreground" toml:"foreground"`
|
||||
}
|
||||
|
||||
type ColorHistory []*ColorSet
|
||||
type History []*Set
|
||||
|
||||
func (c *ColorHistory) Len() int {
|
||||
func (c *History) Len() int {
|
||||
return len(*c)
|
||||
}
|
||||
|
||||
func (c *ColorHistory) Add(background, foreground Color) {
|
||||
colors := &ColorSet{
|
||||
func (c *History) Add(background, foreground Ansi) {
|
||||
colors := &Set{
|
||||
Foreground: foreground,
|
||||
Background: background,
|
||||
}
|
||||
|
@ -50,7 +52,7 @@ func (c *ColorHistory) Add(background, foreground Color) {
|
|||
*c = append(*c, colors)
|
||||
}
|
||||
|
||||
func (c *ColorHistory) Pop() {
|
||||
func (c *History) Pop() {
|
||||
if c.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -58,7 +60,7 @@ func (c *ColorHistory) Pop() {
|
|||
*c = (*c)[:c.Len()-1]
|
||||
}
|
||||
|
||||
func (c *ColorHistory) Background() Color {
|
||||
func (c *History) Background() Ansi {
|
||||
if c.Len() == 0 {
|
||||
return emptyColor
|
||||
}
|
||||
|
@ -66,7 +68,7 @@ func (c *ColorHistory) Background() Color {
|
|||
return (*c)[c.Len()-1].Background
|
||||
}
|
||||
|
||||
func (c *ColorHistory) Foreground() Color {
|
||||
func (c *History) Foreground() Ansi {
|
||||
if c.Len() == 0 {
|
||||
return emptyColor
|
||||
}
|
||||
|
@ -74,44 +76,70 @@ func (c *ColorHistory) Foreground() Color {
|
|||
return (*c)[c.Len()-1].Foreground
|
||||
}
|
||||
|
||||
// Color is an ANSI color code ready to be printed to the console.
|
||||
// Ansi is an ANSI color code ready to be printed to the console.
|
||||
// Example: "38;2;255;255;255", "48;2;255;255;255", "31", "95".
|
||||
type Color string
|
||||
type Ansi string
|
||||
|
||||
const (
|
||||
emptyColor = Color("")
|
||||
transparentColor = Color(Transparent)
|
||||
emptyColor = Ansi("")
|
||||
)
|
||||
|
||||
func (c Color) IsEmpty() bool {
|
||||
func (c Ansi) IsEmpty() bool {
|
||||
return c == emptyColor
|
||||
}
|
||||
|
||||
func (c Color) IsTransparent() bool {
|
||||
return c == transparentColor
|
||||
func (c Ansi) IsTransparent() bool {
|
||||
return c == Transparent
|
||||
}
|
||||
|
||||
func (c Color) IsClear() bool {
|
||||
return c == transparentColor || c == emptyColor
|
||||
func (c Ansi) IsClear() bool {
|
||||
return c == Transparent || c == emptyColor
|
||||
}
|
||||
|
||||
func (c Color) ToForeground() Color {
|
||||
colorString := string(c)
|
||||
func (c Ansi) ToForeground() Ansi {
|
||||
colorString := c.String()
|
||||
if strings.HasPrefix(colorString, "38;") {
|
||||
return Color(strings.Replace(colorString, "38;", "48;", 1))
|
||||
return Ansi(strings.Replace(colorString, "38;", "48;", 1))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func MakeColors(palette Palette, cacheEnabled bool, accentColor string, env platform.Environment) (colors ColorString) {
|
||||
defaultColors := &DefaultColors{}
|
||||
func (c Ansi) ResolveTemplate(env runtime.Environment) Ansi {
|
||||
if c.IsEmpty() {
|
||||
return c
|
||||
}
|
||||
|
||||
if c.IsTransparent() {
|
||||
return emptyColor
|
||||
}
|
||||
|
||||
tmpl := &template.Text{
|
||||
Template: string(c),
|
||||
Context: nil,
|
||||
Env: env,
|
||||
}
|
||||
|
||||
text, err := tmpl.Render()
|
||||
if err != nil {
|
||||
return Transparent
|
||||
}
|
||||
|
||||
return Ansi(text)
|
||||
}
|
||||
|
||||
func (c Ansi) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
func MakeColors(palette Palette, cacheEnabled bool, accentColor Ansi, env runtime.Environment) (colors String) {
|
||||
defaultColors := &Defaults{}
|
||||
defaultColors.SetAccentColor(env, accentColor)
|
||||
colors = defaultColors
|
||||
if palette != nil {
|
||||
colors = &PaletteColors{ansiColors: colors, palette: palette}
|
||||
}
|
||||
if cacheEnabled {
|
||||
colors = &CachedColors{ansiColors: colors}
|
||||
colors = &Cached{ansiColors: colors}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -120,14 +148,14 @@ type RGB struct {
|
|||
R, G, B uint8
|
||||
}
|
||||
|
||||
// DefaultColors is the default AnsiColors implementation.
|
||||
type DefaultColors struct {
|
||||
accent *Colors
|
||||
// Defaults is the default AnsiColors implementation.
|
||||
type Defaults struct {
|
||||
accent *Set
|
||||
}
|
||||
|
||||
var (
|
||||
// Map for color names and their respective foreground [0] or background [1] color codes
|
||||
ansiColorCodes = map[string][2]Color{
|
||||
ansiColorCodes = map[Ansi][2]Ansi{
|
||||
"black": {"30", "40"},
|
||||
"red": {"31", "41"},
|
||||
"green": {"32", "42"},
|
||||
|
@ -153,103 +181,121 @@ const (
|
|||
backgroundIndex = 1
|
||||
)
|
||||
|
||||
func (d *DefaultColors) ToColor(colorString string, isBackground, trueColor bool) Color {
|
||||
if len(colorString) == 0 {
|
||||
func (d *Defaults) ToAnsi(ansiColor Ansi, isBackground bool) Ansi {
|
||||
if len(ansiColor) == 0 {
|
||||
return emptyColor
|
||||
}
|
||||
if colorString == Transparent {
|
||||
return transparentColor
|
||||
|
||||
if ansiColor.IsTransparent() {
|
||||
return ansiColor
|
||||
}
|
||||
if colorString == Accent {
|
||||
|
||||
if ansiColor == Accent {
|
||||
if d.accent == nil {
|
||||
return emptyColor
|
||||
}
|
||||
|
||||
if isBackground {
|
||||
return Color(d.accent.Background)
|
||||
return d.accent.Background
|
||||
}
|
||||
return Color(d.accent.Foreground)
|
||||
|
||||
return d.accent.Foreground
|
||||
}
|
||||
colorFromName, err := getAnsiColorFromName(colorString, isBackground)
|
||||
|
||||
colorFromName, err := getAnsiColorFromName(ansiColor, isBackground)
|
||||
if err == nil {
|
||||
return colorFromName
|
||||
}
|
||||
|
||||
colorString := ansiColor.String()
|
||||
|
||||
if !strings.HasPrefix(colorString, "#") {
|
||||
val, err := strconv.ParseUint(colorString, 10, 64)
|
||||
if err != nil || val > 255 {
|
||||
return emptyColor
|
||||
}
|
||||
|
||||
c256 := color.C256(uint8(val), isBackground)
|
||||
return Color(c256.String())
|
||||
return Ansi(c256.String())
|
||||
}
|
||||
|
||||
style := color.HEX(colorString, isBackground)
|
||||
if !style.IsEmpty() {
|
||||
if trueColor {
|
||||
return Color(style.String())
|
||||
if TrueColor {
|
||||
return Ansi(style.String())
|
||||
}
|
||||
return Color(style.C256().String())
|
||||
|
||||
return Ansi(style.C256().String())
|
||||
}
|
||||
|
||||
if colorInt, err := strconv.ParseInt(colorString, 10, 8); err == nil {
|
||||
c := color.C256(uint8(colorInt), isBackground)
|
||||
return Color(c.String())
|
||||
|
||||
return Ansi(c.String())
|
||||
}
|
||||
|
||||
return emptyColor
|
||||
}
|
||||
|
||||
// getAnsiColorFromName returns the color code for a given color name if the name is
|
||||
// known ANSI color name.
|
||||
func getAnsiColorFromName(colorName string, isBackground bool) (Color, error) {
|
||||
if colorCodes, found := ansiColorCodes[colorName]; found {
|
||||
func getAnsiColorFromName(colorValue Ansi, isBackground bool) (Ansi, error) {
|
||||
if colorCodes, found := ansiColorCodes[colorValue]; found {
|
||||
if isBackground {
|
||||
return colorCodes[backgroundIndex], nil
|
||||
}
|
||||
|
||||
return colorCodes[foregroundIndex], nil
|
||||
}
|
||||
return "", fmt.Errorf("color name %s does not exist", colorName)
|
||||
|
||||
return "", fmt.Errorf("color name %s does not exist", colorValue)
|
||||
}
|
||||
|
||||
func IsAnsiColorName(colorString string) bool {
|
||||
_, ok := ansiColorCodes[colorString]
|
||||
func IsAnsiColorName(colorValue Ansi) bool {
|
||||
_, ok := ansiColorCodes[colorValue]
|
||||
return ok
|
||||
}
|
||||
|
||||
// PaletteColors is the AnsiColors Decorator that uses the Palette to do named color
|
||||
// lookups before ANSI color code generation.
|
||||
type PaletteColors struct {
|
||||
ansiColors ColorString
|
||||
ansiColors String
|
||||
palette Palette
|
||||
}
|
||||
|
||||
func (p *PaletteColors) ToColor(colorString string, isBackground, trueColor bool) Color {
|
||||
func (p *PaletteColors) ToAnsi(colorString Ansi, isBackground bool) Ansi {
|
||||
paletteColor, err := p.palette.ResolveColor(colorString)
|
||||
if err != nil {
|
||||
return emptyColor
|
||||
}
|
||||
ansiColor := p.ansiColors.ToColor(paletteColor, isBackground, trueColor)
|
||||
|
||||
ansiColor := p.ansiColors.ToAnsi(paletteColor, isBackground)
|
||||
|
||||
return ansiColor
|
||||
}
|
||||
|
||||
// CachedColors is the AnsiColors Decorator that does simple color lookup caching.
|
||||
// Cached is the AnsiColors Decorator that does simple color lookup caching.
|
||||
// ToColor calls are cheap, but not free, and having a simple cache in
|
||||
// has measurable positive effect on performance.
|
||||
type CachedColors struct {
|
||||
ansiColors ColorString
|
||||
colorCache map[cachedColorKey]Color
|
||||
type Cached struct {
|
||||
ansiColors String
|
||||
colorCache map[cachedColorKey]Ansi
|
||||
}
|
||||
|
||||
type cachedColorKey struct {
|
||||
colorString string
|
||||
colorString Ansi
|
||||
isBackground bool
|
||||
}
|
||||
|
||||
func (c *CachedColors) ToColor(colorString string, isBackground, trueColor bool) Color {
|
||||
func (c *Cached) ToAnsi(colorString Ansi, isBackground bool) Ansi {
|
||||
if c.colorCache == nil {
|
||||
c.colorCache = make(map[cachedColorKey]Color)
|
||||
c.colorCache = make(map[cachedColorKey]Ansi)
|
||||
}
|
||||
key := cachedColorKey{colorString, isBackground}
|
||||
if ansiColor, hit := c.colorCache[key]; hit {
|
||||
return ansiColor
|
||||
}
|
||||
ansiColor := c.ansiColors.ToColor(colorString, isBackground, trueColor)
|
||||
ansiColor := c.ansiColors.ToAnsi(colorString, isBackground)
|
||||
c.colorCache[key] = ansiColor
|
||||
return ansiColor
|
||||
}
|
89
src/color/colors_test.go
Normal file
89
src/color/colors_test.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
|
||||
|
||||
testify_ "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestGetAnsiFromColorString(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Expected Ansi
|
||||
Color Ansi
|
||||
Background bool
|
||||
Color256 bool
|
||||
}{
|
||||
{Case: "256 color", Expected: Ansi("38;5;99"), Color: "99", Background: false},
|
||||
{Case: "256 color", Expected: Ansi("38;5;122"), Color: "122", Background: false},
|
||||
{Case: "Invalid background", Expected: emptyColor, Color: "invalid", Background: true},
|
||||
{Case: "Invalid background", Expected: emptyColor, Color: "invalid", Background: false},
|
||||
{Case: "Hex foreground", Expected: Ansi("38;2;170;187;204"), Color: "#AABBCC", Background: false},
|
||||
{Case: "Hex backgrond", Expected: Ansi("48;2;170;187;204"), Color: "#AABBCC", Background: true},
|
||||
{Case: "Base 8 foreground", Expected: Ansi("31"), Color: "red", Background: false},
|
||||
{Case: "Base 8 background", Expected: Ansi("41"), Color: "red", Background: true},
|
||||
{Case: "Base 16 foreground", Expected: Ansi("91"), Color: "lightRed", Background: false},
|
||||
{Case: "Base 16 backround", Expected: Ansi("101"), Color: "lightRed", Background: true},
|
||||
{Case: "Non true color TERM", Expected: Ansi("38;5;146"), Color: "#AABBCC", Color256: true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
ansiColors := &Defaults{}
|
||||
TrueColor = !tc.Color256
|
||||
ansiColor := ansiColors.ToAnsi(tc.Color, tc.Background)
|
||||
assert.Equal(t, tc.Expected, ansiColor, tc.Case)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeColors(t *testing.T) {
|
||||
env := &mock.Environment{}
|
||||
|
||||
env.On("WindowsRegistryKeyValue", `HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`).Return(&runtime.WindowsRegistryValue{}, errors.New("err"))
|
||||
colors := MakeColors(nil, false, "", env)
|
||||
assert.IsType(t, &Defaults{}, colors)
|
||||
|
||||
colors = MakeColors(nil, true, "", env)
|
||||
assert.IsType(t, &Cached{}, colors)
|
||||
assert.IsType(t, &Defaults{}, colors.(*Cached).ansiColors)
|
||||
|
||||
colors = MakeColors(testPalette, false, "", env)
|
||||
assert.IsType(t, &PaletteColors{}, colors)
|
||||
assert.IsType(t, &Defaults{}, colors.(*PaletteColors).ansiColors)
|
||||
|
||||
colors = MakeColors(testPalette, true, "", env)
|
||||
assert.IsType(t, &Cached{}, colors)
|
||||
assert.IsType(t, &PaletteColors{}, colors.(*Cached).ansiColors)
|
||||
assert.IsType(t, &Defaults{}, colors.(*Cached).ansiColors.(*PaletteColors).ansiColors)
|
||||
}
|
||||
|
||||
func TestAnsiRender(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Expected Ansi
|
||||
Term string
|
||||
}{
|
||||
{Case: "Inside vscode", Expected: "#123456", Term: "vscode"},
|
||||
{Case: "Outside vscode", Expected: "", Term: "windowsterminal"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
env := new(mock.Environment)
|
||||
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
|
||||
env.On("TemplateCache").Return(&cache.Template{
|
||||
Env: map[string]string{
|
||||
"TERM_PROGRAM": tc.Term,
|
||||
},
|
||||
})
|
||||
env.On("Flags").Return(&runtime.Flags{})
|
||||
|
||||
ansi := Ansi("{{ if eq \"vscode\" .Env.TERM_PROGRAM }}#123456{{end}}")
|
||||
got := ansi.ResolveTemplate(env)
|
||||
|
||||
assert.Equal(t, tc.Expected, got, tc.Case)
|
||||
}
|
||||
}
|
20
src/color/colors_unix.go
Normal file
20
src/color/colors_unix.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
//go:build !windows
|
||||
|
||||
package color
|
||||
|
||||
import "github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
func GetAccentColor(_ runtime.Environment) (*RGB, error) {
|
||||
return nil, &runtime.NotImplemented{}
|
||||
}
|
||||
|
||||
func (d *Defaults) SetAccentColor(_ runtime.Environment, defaultColor Ansi) {
|
||||
if len(defaultColor) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
d.accent = &Set{
|
||||
Foreground: d.ToAnsi(defaultColor, false),
|
||||
Background: d.ToAnsi(defaultColor, true),
|
||||
}
|
||||
}
|
|
@ -1,22 +1,23 @@
|
|||
package ansi
|
||||
package color
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
)
|
||||
|
||||
func GetAccentColor(env platform.Environment) (*RGB, error) {
|
||||
func GetAccentColor(env runtime.Environment) (*RGB, error) {
|
||||
if env == nil {
|
||||
return nil, errors.New("unable to get color without environment")
|
||||
}
|
||||
|
||||
// see https://stackoverflow.com/questions/3560890/vista-7-how-to-get-glass-color
|
||||
value, err := env.WindowsRegistryKeyValue(`HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`)
|
||||
if err != nil || value.ValueType != platform.DWORD {
|
||||
if err != nil || value.ValueType != runtime.DWORD {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RGB{
|
||||
R: byte(value.DWord >> 16),
|
||||
G: byte(value.DWord >> 8),
|
||||
|
@ -24,19 +25,22 @@ func GetAccentColor(env platform.Environment) (*RGB, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (d *DefaultColors) SetAccentColor(env platform.Environment, defaultColor string) {
|
||||
func (d *Defaults) SetAccentColor(env runtime.Environment, defaultColor Ansi) {
|
||||
rgb, err := GetAccentColor(env)
|
||||
if err != nil {
|
||||
d.accent = &Colors{
|
||||
Foreground: string(d.ToColor(defaultColor, false, env.Flags().TrueColor)),
|
||||
Background: string(d.ToColor(defaultColor, true, env.Flags().TrueColor)),
|
||||
d.accent = &Set{
|
||||
Foreground: d.ToAnsi(defaultColor, false),
|
||||
Background: d.ToAnsi(defaultColor, true),
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
foreground := color.RGB(rgb.R, rgb.G, rgb.B, false)
|
||||
background := color.RGB(rgb.R, rgb.G, rgb.B, true)
|
||||
d.accent = &Colors{
|
||||
Foreground: foreground.String(),
|
||||
Background: background.String(),
|
||||
|
||||
d.accent = &Set{
|
||||
Foreground: Ansi(foreground.String()),
|
||||
Background: Ansi(background.String()),
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package ansi
|
||||
package color
|
||||
|
||||
type Cycle []*Colors
|
||||
type Cycle []*Set
|
||||
|
||||
func (c Cycle) Loop() (*Colors, Cycle) {
|
||||
func (c Cycle) Loop() (*Set, Cycle) {
|
||||
if len(c) == 0 {
|
||||
return nil, c
|
||||
}
|
77
src/color/keywords.go
Normal file
77
src/color/keywords.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package color
|
||||
|
||||
const (
|
||||
// Transparent implies a transparent color
|
||||
Transparent Ansi = "transparent"
|
||||
// Accent is the OS accent color
|
||||
Accent Ansi = "accent"
|
||||
// ParentBackground takes the previous segment's background color
|
||||
ParentBackground Ansi = "parentBackground"
|
||||
// ParentForeground takes the previous segment's color
|
||||
ParentForeground Ansi = "parentForeground"
|
||||
// Background takes the current segment's background color
|
||||
Background Ansi = "background"
|
||||
// Foreground takes the current segment's foreground color
|
||||
Foreground Ansi = "foreground"
|
||||
)
|
||||
|
||||
func (color Ansi) isKeyword() bool {
|
||||
switch color { //nolint: exhaustive
|
||||
case Transparent, ParentBackground, ParentForeground, Background, Foreground:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (color Ansi) Resolve(current *Set, parents []*Set) Ansi {
|
||||
resolveParentColor := func(keyword Ansi) Ansi {
|
||||
for _, parentColor := range parents {
|
||||
if parentColor == nil {
|
||||
return Transparent
|
||||
}
|
||||
|
||||
switch keyword { //nolint: exhaustive
|
||||
case ParentBackground:
|
||||
keyword = parentColor.Background
|
||||
case ParentForeground:
|
||||
keyword = parentColor.Foreground
|
||||
default:
|
||||
if len(keyword) == 0 {
|
||||
return Transparent
|
||||
}
|
||||
return keyword
|
||||
}
|
||||
}
|
||||
|
||||
if len(keyword) == 0 {
|
||||
return Transparent
|
||||
}
|
||||
|
||||
return keyword
|
||||
}
|
||||
|
||||
resolveKeyword := func(keyword Ansi) Ansi {
|
||||
switch {
|
||||
case keyword == Background && current != nil:
|
||||
return current.Background
|
||||
case keyword == Foreground && current != nil:
|
||||
return current.Foreground
|
||||
case (keyword == ParentBackground || keyword == ParentForeground) && parents != nil:
|
||||
return resolveParentColor(keyword)
|
||||
default:
|
||||
return Transparent
|
||||
}
|
||||
}
|
||||
|
||||
for color.isKeyword() {
|
||||
resolved := resolveKeyword(color)
|
||||
if resolved == color {
|
||||
break
|
||||
}
|
||||
|
||||
color = resolved
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package ansi
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type Palette map[string]string
|
||||
type Palette map[Ansi]Ansi
|
||||
|
||||
const (
|
||||
paletteKeyPrefix = "p:"
|
||||
|
@ -17,12 +17,12 @@ const (
|
|||
|
||||
// ResolveColor gets a color value from the palette using given colorName.
|
||||
// If colorName is not a palette reference, it is returned as is.
|
||||
func (p Palette) ResolveColor(colorName string) (string, error) {
|
||||
func (p Palette) ResolveColor(colorName Ansi) (Ansi, error) {
|
||||
return p.resolveColor(colorName, 1, &colorName)
|
||||
}
|
||||
|
||||
// originalColorName is a pointer to save allocations
|
||||
func (p Palette) resolveColor(colorName string, depth int, originalColorName *string) (string, error) {
|
||||
func (p Palette) resolveColor(colorName Ansi, depth int, originalColorName *Ansi) (Ansi, error) {
|
||||
key, ok := asPaletteKey(colorName)
|
||||
// colorName is not a palette key, return it as is
|
||||
if !ok {
|
||||
|
@ -45,31 +45,31 @@ func (p Palette) resolveColor(colorName string, depth int, originalColorName *st
|
|||
return color, nil
|
||||
}
|
||||
|
||||
func asPaletteKey(colorName string) (string, bool) {
|
||||
func asPaletteKey(colorName Ansi) (Ansi, bool) {
|
||||
prefix, isKey := isPaletteKey(colorName)
|
||||
if !isKey {
|
||||
return "", false
|
||||
}
|
||||
|
||||
key := strings.TrimPrefix(colorName, prefix)
|
||||
key := strings.TrimPrefix(colorName.String(), prefix.String())
|
||||
|
||||
return key, true
|
||||
return Ansi(key), true
|
||||
}
|
||||
|
||||
func isPaletteKey(colorName string) (string, bool) {
|
||||
return paletteKeyPrefix, strings.HasPrefix(colorName, paletteKeyPrefix)
|
||||
func isPaletteKey(colorName Ansi) (Ansi, bool) {
|
||||
return paletteKeyPrefix, strings.HasPrefix(colorName.String(), paletteKeyPrefix)
|
||||
}
|
||||
|
||||
// PaletteKeyError records the missing Palette key.
|
||||
type PaletteKeyError struct {
|
||||
Key string
|
||||
Key Ansi
|
||||
palette Palette
|
||||
}
|
||||
|
||||
func (p *PaletteKeyError) Error() string {
|
||||
keys := make([]string, 0, len(p.palette))
|
||||
for key := range p.palette {
|
||||
keys = append(keys, key)
|
||||
keys = append(keys, key.String())
|
||||
}
|
||||
sort.Strings(keys)
|
||||
allColors := strings.Join(keys, ",")
|
||||
|
@ -80,8 +80,8 @@ func (p *PaletteKeyError) Error() string {
|
|||
// PaletteRecursiveKeyError records the Palette key and resolved color value (which
|
||||
// is also a Palette key)
|
||||
type PaletteRecursiveKeyError struct {
|
||||
Key string
|
||||
Value string
|
||||
Key Ansi
|
||||
Value Ansi
|
||||
depth int
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ func (p *PaletteRecursiveKeyError) Error() string {
|
|||
|
||||
// maybeResolveColor wraps resolveColor and silences possible errors, returning
|
||||
// Transparent color by default, as a Block does not know how to handle color errors.
|
||||
func (p Palette) MaybeResolveColor(colorName string) string {
|
||||
func (p Palette) MaybeResolveColor(colorName Ansi) Ansi {
|
||||
color, err := p.ResolveColor(colorName)
|
||||
if err != nil {
|
||||
return ""
|
|
@ -1,4 +1,4 @@
|
|||
package ansi
|
||||
package color
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
@ -18,9 +18,9 @@ var (
|
|||
|
||||
type TestPaletteRequest struct {
|
||||
Case string
|
||||
Request string
|
||||
Request Ansi
|
||||
ExpectedError bool
|
||||
Expected string
|
||||
Expected Ansi
|
||||
}
|
||||
|
||||
func TestPaletteShouldResolveColorFromTestPalette(t *testing.T) {
|
||||
|
@ -45,7 +45,7 @@ func testPaletteRequest(t *testing.T, tc TestPaletteRequest) {
|
|||
assert.Equal(t, tc.Expected, actual, "expected different color value")
|
||||
} else {
|
||||
assert.NotNil(t, err, tc.Case)
|
||||
assert.Equal(t, tc.Expected, err.Error())
|
||||
assert.Equal(t, string(tc.Expected), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ func TestPaletteShouldResolveRecursiveReference(t *testing.T) {
|
|||
assert.Equal(t, tc.Expected, actual, "expected different color value")
|
||||
} else {
|
||||
assert.NotNil(t, err, "expected error")
|
||||
assert.Equal(t, tc.Expected, err.Error())
|
||||
assert.Equal(t, string(tc.Expected), err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ func TestPaletteShouldHandleEmptyKey(t *testing.T) {
|
|||
actual, err := tp.ResolveColor("p:")
|
||||
|
||||
assert.Nil(t, err, "expected no error")
|
||||
assert.Equal(t, "#000000", actual, "expected different color value")
|
||||
assert.Equal(t, Ansi("#000000"), actual, "expected different color value")
|
||||
}
|
||||
|
||||
func BenchmarkPaletteMixedCaseResolution(b *testing.B) {
|
|
@ -1,4 +1,4 @@
|
|||
package ansi
|
||||
package color
|
||||
|
||||
type Palettes struct {
|
||||
Template string `json:"template,omitempty" toml:"template,omitempty"`
|
106
src/config/backup.go
Normal file
106
src/config/backup.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
func (cfg *Config) Backup() {
|
||||
dst := cfg.origin + ".bak"
|
||||
source, err := os.Open(cfg.origin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer source.Close()
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer destination.Close()
|
||||
_, err = io.Copy(destination, source)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) Export(format string) string {
|
||||
if len(format) != 0 {
|
||||
cfg.Format = format
|
||||
}
|
||||
|
||||
var result bytes.Buffer
|
||||
|
||||
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:
|
||||
tomlEncoder := toml.NewEncoder(&result)
|
||||
tomlEncoder.SetIndentTables(true)
|
||||
|
||||
err := tomlEncoder.Encode(cfg)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// unsupported format
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cfg *Config) BackupAndMigrate() {
|
||||
cfg.Backup()
|
||||
cfg.Migrate()
|
||||
cfg.Write(cfg.Format)
|
||||
}
|
||||
|
||||
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, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
_, err = f.WriteString(content)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
108
src/config/block.go
Normal file
108
src/config/block.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
)
|
||||
|
||||
// BlockType type of block
|
||||
type BlockType string
|
||||
|
||||
// BlockAlignment aligment of a Block
|
||||
type BlockAlignment string
|
||||
|
||||
// Overflow defines how to handle a right block that overflows with the previous block
|
||||
type Overflow string
|
||||
|
||||
const (
|
||||
// Prompt writes one or more Segments
|
||||
Prompt BlockType = "prompt"
|
||||
// LineBreak creates a line break in the prompt
|
||||
LineBreak BlockType = "newline"
|
||||
// RPrompt is a right aligned prompt
|
||||
RPrompt BlockType = "rprompt"
|
||||
// Left aligns left
|
||||
Left BlockAlignment = "left"
|
||||
// Right aligns right
|
||||
Right BlockAlignment = "right"
|
||||
// Break adds a line break
|
||||
Break Overflow = "break"
|
||||
// Hide hides the block
|
||||
Hide Overflow = "hide"
|
||||
)
|
||||
|
||||
// Block defines a part of the prompt with optional segments
|
||||
type Block struct {
|
||||
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"`
|
||||
|
||||
LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"`
|
||||
TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"`
|
||||
|
||||
// Deprecated: keep the logic for legacy purposes
|
||||
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" toml:"max_width,omitempty"`
|
||||
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
|
||||
|
||||
env runtime.Environment
|
||||
}
|
||||
|
||||
func (b *Block) Init(env runtime.Environment) {
|
||||
b.env = env
|
||||
b.executeSegmentLogic()
|
||||
}
|
||||
|
||||
func (b *Block) Enabled() bool {
|
||||
if b.Type == LineBreak {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, segment := range b.Segments {
|
||||
if segment.Enabled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Block) executeSegmentLogic() {
|
||||
if shouldHideForWidth(b.env, b.MinWidth, b.MaxWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
b.setEnabledSegments()
|
||||
b.setSegmentsText()
|
||||
}
|
||||
|
||||
func (b *Block) setEnabledSegments() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.SetEnabled(b.env)
|
||||
}(segment)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) setSegmentsText() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.setText()
|
||||
}(segment)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
138
src/config/config.go
Normal file
138
src/config/config.go
Normal file
|
@ -0,0 +1,138 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"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/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
)
|
||||
|
||||
const (
|
||||
JSON string = "json"
|
||||
YAML string = "yaml"
|
||||
TOML string = "toml"
|
||||
|
||||
Version = 2
|
||||
)
|
||||
|
||||
// Config holds all the theme for rendering the prompt
|
||||
type Config struct {
|
||||
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 color.Ansi `json:"terminal_background,omitempty" toml:"terminal_background,omitempty"`
|
||||
AccentColor color.Ansi `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 color.Palette `json:"palette,omitempty" toml:"palette,omitempty"`
|
||||
Palettes *color.Palettes `json:"palettes,omitempty" toml:"palettes,omitempty"`
|
||||
Cycle color.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"`
|
||||
EnableCursorPositioning bool `json:"enable_cursor_positioning,omitempty" toml:"enable_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"`
|
||||
AutoUpgrade bool `json:"auto_upgrade,omitempty" toml:"auto_upgrade,omitempty"`
|
||||
ITermFeatures terminal.ITermFeatures `json:"iterm_features,omitempty" toml:"iterm_features,omitempty"`
|
||||
|
||||
// Deprecated
|
||||
OSC99 bool `json:"osc99,omitempty" toml:"osc99,omitempty"`
|
||||
|
||||
Output string `json:"-" toml:"-"`
|
||||
MigrateGlyphs bool `json:"-" toml:"-"`
|
||||
Format string `json:"-" toml:"-"`
|
||||
|
||||
origin string
|
||||
// eval bool
|
||||
updated bool
|
||||
env runtime.Environment
|
||||
}
|
||||
|
||||
func (cfg *Config) MakeColors() color.String {
|
||||
cacheDisabled := cfg.env.Getenv("OMP_CACHE_DISABLED") == "1"
|
||||
return color.MakeColors(cfg.getPalette(), !cacheDisabled, cfg.AccentColor, cfg.env)
|
||||
}
|
||||
|
||||
func (cfg *Config) getPalette() color.Palette {
|
||||
if cfg.Palettes == nil {
|
||||
return cfg.Palette
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: cfg.Palettes.Template,
|
||||
Env: cfg.env,
|
||||
}
|
||||
if palette, err := tmpl.Render(); err == nil {
|
||||
if p, ok := cfg.Palettes.List[palette]; ok {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return cfg.Palette
|
||||
}
|
||||
|
||||
func (cfg *Config) Features() shell.Features {
|
||||
var feats shell.Features
|
||||
|
||||
if cfg.TransientPrompt != nil {
|
||||
feats = append(feats, shell.Transient)
|
||||
}
|
||||
|
||||
if cfg.ShellIntegration {
|
||||
feats = append(feats, shell.FTCSMarks)
|
||||
}
|
||||
|
||||
if !cfg.AutoUpgrade && !cfg.DisableNotice {
|
||||
feats = append(feats, shell.Notice)
|
||||
}
|
||||
|
||||
if cfg.AutoUpgrade {
|
||||
feats = append(feats, shell.Upgrade)
|
||||
}
|
||||
|
||||
if cfg.ErrorLine != nil || cfg.ValidLine != nil {
|
||||
feats = append(feats, shell.LineError)
|
||||
}
|
||||
|
||||
if len(cfg.Tooltips) > 0 {
|
||||
feats = append(feats, shell.Tooltips)
|
||||
}
|
||||
|
||||
if cfg.env.Shell() == shell.FISH && cfg.ITermFeatures != nil && cfg.ITermFeatures.Contains(terminal.PromptMark) {
|
||||
feats = append(feats, shell.PromptMark)
|
||||
}
|
||||
|
||||
for i, block := range cfg.Blocks {
|
||||
if (i == 0 && block.Newline) && cfg.EnableCursorPositioning {
|
||||
feats = append(feats, shell.CursorPositioning)
|
||||
}
|
||||
|
||||
if block.Type == RPrompt {
|
||||
feats = append(feats, shell.RPrompt)
|
||||
}
|
||||
|
||||
for _, segment := range block.Segments {
|
||||
if segment.Type == AZ {
|
||||
source := segment.Properties.GetString(segments.Source, segments.FirstMatch)
|
||||
if source == segments.Pwsh || source == segments.FirstMatch {
|
||||
feats = append(feats, shell.Azure)
|
||||
}
|
||||
}
|
||||
|
||||
if segment.Type == GIT {
|
||||
source := segment.Properties.GetString(segments.Source, segments.Cli)
|
||||
if source == segments.Pwsh {
|
||||
feats = append(feats, shell.PoshGit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return feats
|
||||
}
|
91
src/config/config_test.go
Normal file
91
src/config/config_test.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
testify_ "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestGetPalette(t *testing.T) {
|
||||
palette := color.Palette{
|
||||
"red": "#ff0000",
|
||||
"blue": "#0000ff",
|
||||
}
|
||||
cases := []struct {
|
||||
Case string
|
||||
Palettes *color.Palettes
|
||||
Palette color.Palette
|
||||
ExpectedPalette color.Palette
|
||||
}{
|
||||
{
|
||||
Case: "match",
|
||||
Palettes: &color.Palettes{
|
||||
Template: "{{ .Shell }}",
|
||||
List: map[string]color.Palette{
|
||||
"bash": palette,
|
||||
"zsh": {
|
||||
"red": "#ff0001",
|
||||
"blue": "#0000fb",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedPalette: palette,
|
||||
},
|
||||
{
|
||||
Case: "no match, no fallback",
|
||||
Palettes: &color.Palettes{
|
||||
Template: "{{ .Shell }}",
|
||||
List: map[string]color.Palette{
|
||||
"fish": palette,
|
||||
"zsh": {
|
||||
"red": "#ff0001",
|
||||
"blue": "#0000fb",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedPalette: nil,
|
||||
},
|
||||
{
|
||||
Case: "no match, default",
|
||||
Palettes: &color.Palettes{
|
||||
Template: "{{ .Shell }}",
|
||||
List: map[string]color.Palette{
|
||||
"zsh": {
|
||||
"red": "#ff0001",
|
||||
"blue": "#0000fb",
|
||||
},
|
||||
},
|
||||
},
|
||||
Palette: palette,
|
||||
ExpectedPalette: palette,
|
||||
},
|
||||
{
|
||||
Case: "no palettes",
|
||||
ExpectedPalette: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
env := &mock.Environment{}
|
||||
env.On("TemplateCache").Return(&cache.Template{
|
||||
Env: map[string]string{},
|
||||
Shell: "bash",
|
||||
})
|
||||
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
|
||||
env.On("Flags").Return(&runtime.Flags{})
|
||||
|
||||
cfg := &Config{
|
||||
env: env,
|
||||
Palette: tc.Palette,
|
||||
Palettes: tc.Palettes,
|
||||
}
|
||||
|
||||
got := cfg.getPalette()
|
||||
assert.Equal(t, tc.ExpectedPalette, got, tc.Case)
|
||||
}
|
||||
}
|
199
src/config/default.go
Normal file
199
src/config/default.go
Normal file
|
@ -0,0 +1,199 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
)
|
||||
|
||||
func Default(env runtime.Environment, warning bool) *Config {
|
||||
exitBackgroundTemplate := "{{ if gt .Code 0 }}p:red{{ end }}"
|
||||
exitTemplate := " {{ if gt .Code 0 }}\uf00d{{ else }}\uf00c{{ end }} "
|
||||
if warning {
|
||||
exitBackgroundTemplate = "p:red"
|
||||
exitTemplate = " CONFIG ERROR "
|
||||
}
|
||||
cfg := &Config{
|
||||
Version: 2,
|
||||
FinalSpace: true,
|
||||
Blocks: []*Block{
|
||||
{
|
||||
Type: Prompt,
|
||||
Alignment: Left,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: SESSION,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b6",
|
||||
TrailingDiamond: "\ue0b0",
|
||||
Foreground: "p:black",
|
||||
Background: "p:yellow",
|
||||
Template: " {{ if .SSHSession }}\ueba9 {{ end }}{{ .UserName }} ",
|
||||
},
|
||||
{
|
||||
Type: PATH,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Foreground: "p:white",
|
||||
Background: "p:orange",
|
||||
Properties: properties.Map{
|
||||
properties.Style: "folder",
|
||||
},
|
||||
Template: " \uea83 {{ path .Path .Location }} ",
|
||||
},
|
||||
{
|
||||
Type: GIT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Foreground: "p:black",
|
||||
Background: "p:green",
|
||||
BackgroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:yellow{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:red{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}#49416D{{ end }}",
|
||||
"{{ if gt .Behind 0 }}#7A306C{{ end }}",
|
||||
},
|
||||
ForegroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:black{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:white{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}p:white{{ end }}",
|
||||
},
|
||||
Properties: properties.Map{
|
||||
segments.BranchMaxLength: 25,
|
||||
segments.FetchStatus: true,
|
||||
segments.FetchUpstreamIcon: true,
|
||||
},
|
||||
Template: " {{ if .UpstreamURL }}{{ url .UpstreamIcon .UpstreamURL }} {{ end }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} \uf044 {{ .Working.String }}{{ end }}{{ if .Staging.Changed }} \uf046 {{ .Staging.String }}{{ end }} ", //nolint:lll
|
||||
},
|
||||
{
|
||||
Type: ROOT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Foreground: "p:white",
|
||||
Background: "p:yellow",
|
||||
Template: " \uf0e7 ",
|
||||
},
|
||||
{
|
||||
Type: STATUS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "<transparent,background>\ue0b0</>",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Foreground: "p:white",
|
||||
Background: "p:blue",
|
||||
BackgroundTemplates: []string{
|
||||
exitBackgroundTemplate,
|
||||
},
|
||||
Properties: properties.Map{
|
||||
properties.AlwaysEnabled: true,
|
||||
},
|
||||
Template: exitTemplate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: RPrompt,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: NODE,
|
||||
Style: Plain,
|
||||
Foreground: "p:green",
|
||||
Background: "transparent",
|
||||
Template: "\ue718 ",
|
||||
Properties: properties.Map{
|
||||
segments.HomeEnabled: false,
|
||||
segments.FetchPackageManager: false,
|
||||
segments.DisplayMode: "files",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: GOLANG,
|
||||
Style: Plain,
|
||||
Foreground: "p:blue",
|
||||
Background: "transparent",
|
||||
Template: "\ue626 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: PYTHON,
|
||||
Style: Plain,
|
||||
Foreground: "p:yellow",
|
||||
Background: "transparent",
|
||||
Template: "\ue235 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
segments.DisplayMode: "files",
|
||||
segments.FetchVirtualEnv: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: SHELL,
|
||||
Style: Plain,
|
||||
Foreground: "p:white",
|
||||
Background: "transparent",
|
||||
Template: "in <p:blue><b>{{ .Name }}</b></> ",
|
||||
},
|
||||
{
|
||||
Type: TIME,
|
||||
Style: Plain,
|
||||
Foreground: "p:white",
|
||||
Background: "transparent",
|
||||
Template: "at <p:blue><b>{{ .CurrentDate | date \"15:04:05\" }}</b></>",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ConsoleTitleTemplate: "{{ .Shell }} in {{ .Folder }}",
|
||||
Palette: color.Palette{
|
||||
"black": "#262B44",
|
||||
"blue": "#4B95E9",
|
||||
"green": "#59C9A5",
|
||||
"orange": "#F07623",
|
||||
"red": "#D81E5B",
|
||||
"white": "#E0DEF4",
|
||||
"yellow": "#F3AE35",
|
||||
},
|
||||
SecondaryPrompt: &Segment{
|
||||
Foreground: "p:black",
|
||||
Background: "transparent",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> > </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
TransientPrompt: &Segment{
|
||||
Foreground: "p:black",
|
||||
Background: "transparent",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> {{ .Folder }} </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
Tooltips: []*Segment{
|
||||
{
|
||||
Type: AWS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Foreground: "p:white",
|
||||
Background: "p:orange",
|
||||
Template: " \ue7ad {{ .Profile }}{{ if .Region }}@{{ .Region }}{{ end }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"aws"},
|
||||
},
|
||||
{
|
||||
Type: AZ,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Foreground: "p:white",
|
||||
Background: "p:blue",
|
||||
Template: " \uebd8 {{ .Name }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"az"},
|
||||
},
|
||||
},
|
||||
}
|
||||
cfg.env = env
|
||||
return cfg
|
||||
}
|
97
src/config/load.go
Normal file
97
src/config/load.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
stdOS "os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gookit/goutil/jsonutil"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
|
||||
json "github.com/goccy/go-json"
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
// LoadConfig returns the default configuration including possible user overrides
|
||||
func Load(env runtime.Environment) *Config {
|
||||
cfg := loadConfig(env)
|
||||
|
||||
// only migrate automatically when the switch isn't set
|
||||
if !env.Flags().Migrate && cfg.Version < Version {
|
||||
cfg.BackupAndMigrate()
|
||||
}
|
||||
|
||||
if !cfg.ShellIntegration {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// bash - ok
|
||||
// fish - ok
|
||||
// pwsh - ok
|
||||
// zsh - ok
|
||||
// cmd - ok, as of v1.4.25 (chrisant996/clink#457, fixed in chrisant996/clink@8a5d7ea)
|
||||
// nu - built-in (and bugged) feature - nushell/nushell#5585, https://www.nushell.sh/blog/2022-08-16-nushell-0_67.html#shell-integration-fdncred-and-tyriar
|
||||
// elv - broken OSC sequences
|
||||
// xonsh - broken OSC sequences
|
||||
// tcsh - overall broken, FTCS_COMMAND_EXECUTED could be added to POSH_POSTCMD in the future
|
||||
switch env.Shell() {
|
||||
case shell.ELVISH, shell.XONSH, shell.TCSH, shell.NU:
|
||||
cfg.ShellIntegration = false
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func loadConfig(env runtime.Environment) *Config {
|
||||
defer env.Trace(time.Now())
|
||||
configFile := env.Flags().Config
|
||||
|
||||
if len(configFile) == 0 {
|
||||
env.Debug("no config file specified, using default")
|
||||
return Default(env, false)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
cfg.origin = configFile
|
||||
cfg.Format = strings.TrimPrefix(filepath.Ext(configFile), ".")
|
||||
cfg.env = env
|
||||
|
||||
data, err := stdOS.ReadFile(configFile)
|
||||
if err != nil {
|
||||
env.Error(err)
|
||||
return Default(env, true)
|
||||
}
|
||||
|
||||
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)
|
||||
return Default(env, true)
|
||||
}
|
||||
|
||||
setDisableNotice(data, &cfg)
|
||||
|
||||
return &cfg
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
)
|
||||
|
||||
|
@ -30,10 +30,10 @@ func (cfg *Config) Migrate() {
|
|||
cfg.ConsoleTitleTemplate = strings.ReplaceAll(cfg.ConsoleTitleTemplate, ".Path", ".PWD")
|
||||
}
|
||||
cfg.updated = true
|
||||
cfg.Version = configVersion
|
||||
cfg.Version = Version
|
||||
}
|
||||
|
||||
func (segment *Segment) migrate(env platform.Environment, version int) {
|
||||
func (segment *Segment) migrate(env runtime.Environment, version int) {
|
||||
if version < 1 {
|
||||
segment.migrationOne(env)
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ func (segment *Segment) migrate(env platform.Environment, version int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (segment *Segment) migrationOne(env platform.Environment) {
|
||||
if err := segment.mapSegmentWithWriter(env); err != nil {
|
||||
func (segment *Segment) migrationOne(env runtime.Environment) {
|
||||
if err := segment.MapSegmentWithWriter(env); err != nil {
|
||||
return
|
||||
}
|
||||
// General properties that need replacement
|
||||
|
@ -162,8 +162,8 @@ func (segment *Segment) migrationOne(env platform.Environment) {
|
|||
delete(segment.Properties, colorBackground)
|
||||
}
|
||||
|
||||
func (segment *Segment) migrationTwo(env platform.Environment) {
|
||||
if err := segment.mapSegmentWithWriter(env); err != nil {
|
||||
func (segment *Segment) migrationTwo(env runtime.Environment) {
|
||||
if err := segment.MapSegmentWithWriter(env); err != nil {
|
||||
return
|
||||
}
|
||||
if !segment.hasProperty(segmentTemplate) {
|
140
src/config/migrate_glyphs.go
Normal file
140
src/config/migrate_glyphs.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
httplib "net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/http"
|
||||
)
|
||||
|
||||
type ConnectionError struct {
|
||||
reason string
|
||||
}
|
||||
|
||||
func (f *ConnectionError) Error() string {
|
||||
return f.reason
|
||||
}
|
||||
|
||||
type codePoints map[uint64]uint64
|
||||
|
||||
func getGlyphCodePoints() (codePoints, error) {
|
||||
var codePoints = make(codePoints)
|
||||
|
||||
ctx, cncl := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
|
||||
defer cncl()
|
||||
|
||||
request, err := httplib.NewRequestWithContext(ctx, httplib.MethodGet, "https://ohmyposh.dev/codepoints.csv", nil)
|
||||
if err != nil {
|
||||
return codePoints, &ConnectionError{reason: err.Error()}
|
||||
}
|
||||
|
||||
response, err := http.HTTPClient.Do(request)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
bytes, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(bytes), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ",")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
oldGlyph, err := strconv.ParseUint(fields[0], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newGlyph, err := strconv.ParseUint(fields[1], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
codePoints[oldGlyph] = newGlyph
|
||||
}
|
||||
|
||||
return codePoints, nil
|
||||
}
|
||||
|
||||
func escapeGlyphs(s string, migrate bool) string {
|
||||
shouldExclude := func(r rune) bool {
|
||||
if r < 0x1000 { // Basic Multilingual Plane
|
||||
return true
|
||||
}
|
||||
if r > 0x1F600 && r < 0x1F64F { // Emoticons
|
||||
return true
|
||||
}
|
||||
if r > 0x1F300 && r < 0x1F5FF { // Misc Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F680 && r < 0x1F6FF { // Transport and Map
|
||||
return true
|
||||
}
|
||||
if r > 0x2600 && r < 0x26FF { // Misc symbols
|
||||
return true
|
||||
}
|
||||
if r > 0x2700 && r < 0x27BF { // Dingbats
|
||||
return true
|
||||
}
|
||||
if r > 0xFE00 && r < 0xFE0F { // Variation Selectors
|
||||
return true
|
||||
}
|
||||
if r > 0x1F900 && r < 0x1F9FF { // Supplemental Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F1E6 && r < 0x1F1FF { // Flags
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var cp codePoints
|
||||
var err error
|
||||
if migrate {
|
||||
cp, err = getGlyphCodePoints()
|
||||
if err != nil {
|
||||
migrate = false
|
||||
}
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
for _, r := range s {
|
||||
// exclude regular characters and emojis
|
||||
if shouldExclude(r) {
|
||||
builder.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
if migrate {
|
||||
if val, OK := cp[uint64(r)]; OK {
|
||||
r = rune(val)
|
||||
}
|
||||
}
|
||||
|
||||
if r > 0x10000 {
|
||||
// calculate surrogate pairs
|
||||
one := 0xd800 + (((r - 0x10000) >> 10) & 0x3ff)
|
||||
two := 0xdc00 + ((r - 0x10000) & 0x3ff)
|
||||
quoted := fmt.Sprintf("\\u%04x\\u%04x", one, two)
|
||||
builder.WriteString(quoted)
|
||||
continue
|
||||
}
|
||||
|
||||
quoted := fmt.Sprintf("\\u%04x", r)
|
||||
builder.WriteString(quoted)
|
||||
}
|
||||
return builder.String()
|
||||
}
|
35
src/config/migrate_glyphs_test.go
Normal file
35
src/config/migrate_glyphs_test.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetCodePoints(t *testing.T) {
|
||||
codepoints, err := getGlyphCodePoints()
|
||||
if connectionError, ok := err.(*ConnectionError); ok {
|
||||
t.Log(connectionError.Error())
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 1939, len(codepoints))
|
||||
}
|
||||
|
||||
func TestEscapeGlyphs(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Expected string
|
||||
}{
|
||||
{Input: "", Expected: "\\udb80\\ude4b"},
|
||||
{Input: "a", Expected: "a"},
|
||||
{Input: "\ue0b4", Expected: "\\ue0b4"},
|
||||
{Input: "\ufd03", Expected: "\\ufd03"},
|
||||
{Input: "}", Expected: "}"},
|
||||
{Input: "🏚", Expected: "🏚"},
|
||||
{Input: "\U000F011B", Expected: "\\udb80\\udd1b"},
|
||||
{Input: "", Expected: "\\udb80\\udd1b"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.Expected, escapeGlyphs(tc.Input, false), tc.Input)
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
mock2 "github.com/stretchr/testify/mock"
|
||||
testify_ "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -91,7 +91,7 @@ func (m *MockedWriter) Template() string {
|
|||
return m.template
|
||||
}
|
||||
|
||||
func (m *MockedWriter) Init(_ properties.Properties, _ platform.Environment) {}
|
||||
func (m *MockedWriter) Init(_ properties.Properties, _ runtime.Environment) {}
|
||||
|
||||
func TestIconOverride(t *testing.T) {
|
||||
cases := []struct {
|
||||
|
@ -327,8 +327,8 @@ func TestSegmentTemplateMigration(t *testing.T) {
|
|||
Type: tc.Type,
|
||||
Properties: tc.Props,
|
||||
}
|
||||
env := &mock.MockedEnvironment{}
|
||||
env.On("Debug", mock2.Anything).Return(nil)
|
||||
env := &mock.Environment{}
|
||||
env.On("Debug", testify_.Anything).Return(nil)
|
||||
segment.migrationOne(env)
|
||||
assert.Equal(t, tc.Expected, segment.Properties[segmentTemplate], tc.Case)
|
||||
}
|
||||
|
@ -431,7 +431,7 @@ func TestMigrateConfig(t *testing.T) {
|
|||
for _, tc := range cases {
|
||||
cfg := &Config{
|
||||
ConsoleTitleTemplate: tc.Template,
|
||||
env: &mock.MockedEnvironment{},
|
||||
env: &mock.Environment{},
|
||||
}
|
||||
cfg.Migrate()
|
||||
assert.Equal(t, tc.Expected, cfg.ConsoleTitleTemplate, tc.Case)
|
||||
|
@ -465,7 +465,7 @@ func TestMigrationTwo(t *testing.T) {
|
|||
if tc.Template != "" {
|
||||
segment.Properties[segmentTemplate] = tc.Template
|
||||
}
|
||||
segment.migrationTwo(&mock.MockedEnvironment{})
|
||||
segment.migrationTwo(&mock.Environment{})
|
||||
assert.Equal(t, tc.Expected, segment.Template, tc.Case)
|
||||
assert.NotContains(t, segment.Properties, segmentTemplate, tc.Case)
|
||||
}
|
5
src/config/notice_default.go
Normal file
5
src/config/notice_default.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build !disablenotice
|
||||
|
||||
package config
|
||||
|
||||
func setDisableNotice(_ []byte, _ *Config) {}
|
15
src/config/notice_disable.go
Normal file
15
src/config/notice_disable.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build disablenotice
|
||||
|
||||
package config
|
||||
|
||||
import "bytes"
|
||||
|
||||
func setDisableNotice(stream []byte, cfg *Config) {
|
||||
if !hasKeyInByteStream(stream, "disable_notice") {
|
||||
cfg.DisableNotice = true
|
||||
}
|
||||
}
|
||||
|
||||
func hasKeyInByteStream(data []byte, key string) bool {
|
||||
return bytes.Contains(data, []byte(key))
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import "github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
import "github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
|
||||
func shouldHideForWidth(env platform.Environment, minWidth, maxWidth int) bool {
|
||||
func shouldHideForWidth(env runtime.Environment, minWidth, maxWidth int) bool {
|
||||
if maxWidth == 0 && minWidth == 0 {
|
||||
return false
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ func TestShouldHideForWidth(t *testing.T) {
|
|||
{Case: "Min & Max cols - show", MinWidth: 10, MaxWidth: 20, Width: 11, Expected: false},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
env := new(mock.MockedEnvironment)
|
||||
env := new(mock.Environment)
|
||||
env.On("TerminalWidth").Return(tc.Width, tc.Error)
|
||||
got := shouldHideForWidth(env, tc.MinWidth, tc.MaxWidth)
|
||||
assert.Equal(t, tc.Expected, got, tc.Case)
|
255
src/config/segment.go
Normal file
255
src/config/segment.go
Normal file
|
@ -0,0 +1,255 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
|
||||
c "golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// SegmentStyle the style of segment, for more information, see the constants
|
||||
type SegmentStyle string
|
||||
|
||||
func (s *SegmentStyle) resolve(env runtime.Environment, context any) SegmentStyle {
|
||||
txtTemplate := &template.Text{
|
||||
Context: context,
|
||||
Env: env,
|
||||
}
|
||||
txtTemplate.Template = string(*s)
|
||||
value, err := txtTemplate.Render()
|
||||
// default to Plain
|
||||
if err != nil || len(value) == 0 {
|
||||
return Plain
|
||||
}
|
||||
return SegmentStyle(value)
|
||||
}
|
||||
|
||||
type Segment struct {
|
||||
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"`
|
||||
ForegroundTemplates template.List `json:"foreground_templates,omitempty" toml:"foreground_templates,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"`
|
||||
Background color.Ansi `json:"background" toml:"background"`
|
||||
Foreground color.Ansi `json:"foreground" toml:"foreground"`
|
||||
Newline bool `json:"newline,omitempty" toml:"newline,omitempty"`
|
||||
|
||||
Enabled bool `json:"-" toml:"-"`
|
||||
|
||||
Text string
|
||||
|
||||
env runtime.Environment
|
||||
writer SegmentWriter
|
||||
styleCache SegmentStyle
|
||||
name string
|
||||
|
||||
// debug info
|
||||
Duration time.Duration
|
||||
NameLength int
|
||||
}
|
||||
|
||||
func (segment *Segment) Name() string {
|
||||
if len(segment.name) != 0 {
|
||||
return segment.name
|
||||
}
|
||||
|
||||
name := segment.Alias
|
||||
if len(name) == 0 {
|
||||
name = c.Title(language.English).String(string(segment.Type))
|
||||
}
|
||||
|
||||
segment.name = name
|
||||
return name
|
||||
}
|
||||
|
||||
func (segment *Segment) SetEnabled(env runtime.Environment) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
// display a message explaining omp failed(with the err)
|
||||
message := fmt.Sprintf("\noh-my-posh fatal error rendering %s segment:%s\n\n%s\n", segment.Type, err, debug.Stack())
|
||||
fmt.Println(message)
|
||||
segment.Enabled = true
|
||||
}()
|
||||
|
||||
// segment timings for debug purposes
|
||||
var start time.Time
|
||||
if env.Flags().Debug {
|
||||
start = time.Now()
|
||||
segment.NameLength = len(segment.Name())
|
||||
defer func() {
|
||||
segment.Duration = time.Since(start)
|
||||
}()
|
||||
}
|
||||
|
||||
err := segment.MapSegmentWithWriter(env)
|
||||
if err != nil || !segment.shouldIncludeFolder() {
|
||||
return
|
||||
}
|
||||
|
||||
segment.env.DebugF("segment: %s", segment.Name())
|
||||
|
||||
// validate toggles
|
||||
if toggles, OK := segment.env.Session().Get(cache.TOGGLECACHE); OK && len(toggles) > 0 {
|
||||
list := strings.Split(toggles, ",")
|
||||
for _, toggle := range list {
|
||||
if SegmentType(toggle) == segment.Type || toggle == segment.Alias {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shouldHideForWidth(segment.env, segment.MinWidth, segment.MaxWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
if segment.writer.Enabled() {
|
||||
segment.Enabled = true
|
||||
env.TemplateCache().AddSegmentData(segment.Name(), segment.writer)
|
||||
}
|
||||
}
|
||||
|
||||
func (segment *Segment) setText() {
|
||||
if !segment.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
segment.Text = segment.string()
|
||||
segment.Enabled = len(strings.ReplaceAll(segment.Text, " ", "")) > 0
|
||||
|
||||
if !segment.Enabled {
|
||||
segment.env.TemplateCache().RemoveSegmentData(segment.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (segment *Segment) string() string {
|
||||
var templatesResult string
|
||||
if !segment.Templates.Empty() {
|
||||
templatesResult = segment.Templates.Resolve(segment.writer, segment.env, "", segment.TemplatesLogic)
|
||||
if len(segment.Template) == 0 {
|
||||
return templatesResult
|
||||
}
|
||||
}
|
||||
|
||||
if len(segment.Template) == 0 {
|
||||
segment.Template = segment.writer.Template()
|
||||
}
|
||||
|
||||
tmpl := &template.Text{
|
||||
Template: segment.Template,
|
||||
Context: segment.writer,
|
||||
Env: segment.env,
|
||||
TemplatesResult: templatesResult,
|
||||
}
|
||||
|
||||
text, err := tmpl.Render()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func (segment *Segment) shouldIncludeFolder() bool {
|
||||
if segment.env == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
cwdIncluded := segment.cwdIncluded()
|
||||
cwdExcluded := segment.cwdExcluded()
|
||||
|
||||
return cwdIncluded && !cwdExcluded
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdIncluded() bool {
|
||||
value, ok := segment.Properties[properties.IncludeFolders]
|
||||
if !ok {
|
||||
// IncludeFolders isn't specified, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
list := properties.ParseStringArray(value)
|
||||
|
||||
if len(list) == 0 {
|
||||
// IncludeFolders is an empty array, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdExcluded() bool {
|
||||
value, ok := segment.Properties[properties.ExcludeFolders]
|
||||
if !ok {
|
||||
value = segment.Properties[properties.IgnoreFolders]
|
||||
}
|
||||
|
||||
list := properties.ParseStringArray(value)
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) ResolveForeground() color.Ansi {
|
||||
if len(segment.ForegroundTemplates) != 0 {
|
||||
match := segment.ForegroundTemplates.FirstMatch(segment.writer, segment.env, segment.Foreground.String())
|
||||
segment.Foreground = color.Ansi(match)
|
||||
}
|
||||
|
||||
return segment.Foreground
|
||||
}
|
||||
|
||||
func (segment *Segment) ResolveBackground() color.Ansi {
|
||||
if len(segment.BackgroundTemplates) != 0 {
|
||||
match := segment.BackgroundTemplates.FirstMatch(segment.writer, segment.env, segment.Background.String())
|
||||
segment.Background = color.Ansi(match)
|
||||
}
|
||||
|
||||
return segment.Background
|
||||
}
|
||||
|
||||
func (segment *Segment) ResolveStyle() SegmentStyle {
|
||||
if len(segment.styleCache) != 0 {
|
||||
return segment.styleCache
|
||||
}
|
||||
|
||||
segment.styleCache = segment.Style.resolve(segment.env, segment.writer)
|
||||
|
||||
return segment.styleCache
|
||||
}
|
||||
|
||||
func (segment *Segment) IsPowerline() bool {
|
||||
style := segment.ResolveStyle()
|
||||
return style == Powerline || style == Accordion
|
||||
}
|
||||
|
||||
func (segment *Segment) HasEmptyDiamondAtEnd() bool {
|
||||
if segment.ResolveStyle() != Diamond {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(segment.TrailingDiamond) == 0
|
||||
}
|
|
@ -1,16 +1,18 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/color"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/properties"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/mock"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
mock2 "github.com/stretchr/testify/mock"
|
||||
testify_ "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -21,8 +23,8 @@ func TestMapSegmentWriterCanMap(t *testing.T) {
|
|||
sc := &Segment{
|
||||
Type: SESSION,
|
||||
}
|
||||
env := new(mock.MockedEnvironment)
|
||||
err := sc.mapSegmentWithWriter(env)
|
||||
env := new(mock.Environment)
|
||||
err := sc.MapSegmentWithWriter(env)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, sc.writer)
|
||||
}
|
||||
|
@ -31,8 +33,8 @@ func TestMapSegmentWriterCannotMap(t *testing.T) {
|
|||
sc := &Segment{
|
||||
Type: "nilwriter",
|
||||
}
|
||||
env := new(mock.MockedEnvironment)
|
||||
err := sc.mapSegmentWithWriter(env)
|
||||
env := new(mock.Environment)
|
||||
err := sc.MapSegmentWithWriter(env)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
@ -71,8 +73,8 @@ func TestShouldIncludeFolder(t *testing.T) {
|
|||
{Case: "!Include & !Exclude", Included: false, Excluded: false, Expected: false},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
env := new(mock.MockedEnvironment)
|
||||
env.On("GOOS").Return(platform.LINUX)
|
||||
env := new(mock.Environment)
|
||||
env.On("GOOS").Return(runtime.LINUX)
|
||||
env.On("Home").Return("")
|
||||
env.On("Pwd").Return(cwd)
|
||||
env.On("DirMatchesOneOf", cwd, []string{"Projects/oh-my-posh"}).Return(tc.Included)
|
||||
|
@ -91,39 +93,39 @@ func TestShouldIncludeFolder(t *testing.T) {
|
|||
|
||||
func TestGetColors(t *testing.T) {
|
||||
cases := []struct {
|
||||
Case string
|
||||
Background bool
|
||||
ExpectedColor string
|
||||
Templates []string
|
||||
DefaultColor string
|
||||
Region string
|
||||
Profile string
|
||||
Case string
|
||||
Background bool
|
||||
Expected color.Ansi
|
||||
Templates []string
|
||||
Default color.Ansi
|
||||
Region string
|
||||
Profile string
|
||||
}{
|
||||
{Case: "No template - foreground", ExpectedColor: "color", Background: false, DefaultColor: "color"},
|
||||
{Case: "No template - background", ExpectedColor: "color", Background: true, DefaultColor: "color"},
|
||||
{Case: "Nil template", ExpectedColor: "color", DefaultColor: "color", Templates: nil},
|
||||
{Case: "No template - foreground", Expected: "color", Background: false, Default: "color"},
|
||||
{Case: "No template - background", Expected: "color", Background: true, Default: "color"},
|
||||
{Case: "Nil template", Expected: "color", Default: "color", Templates: nil},
|
||||
{
|
||||
Case: "Template - default",
|
||||
ExpectedColor: "color",
|
||||
DefaultColor: "color",
|
||||
Case: "Template - default",
|
||||
Expected: "color",
|
||||
Default: "color",
|
||||
Templates: []string{
|
||||
"{{if contains \"john\" .Profile}}color2{{end}}",
|
||||
},
|
||||
Profile: "doe",
|
||||
},
|
||||
{
|
||||
Case: "Template - override",
|
||||
ExpectedColor: "color2",
|
||||
DefaultColor: "color",
|
||||
Case: "Template - override",
|
||||
Expected: "color2",
|
||||
Default: "color",
|
||||
Templates: []string{
|
||||
"{{if contains \"john\" .Profile}}color2{{end}}",
|
||||
},
|
||||
Profile: "john",
|
||||
},
|
||||
{
|
||||
Case: "Template - override multiple",
|
||||
ExpectedColor: "color3",
|
||||
DefaultColor: "color",
|
||||
Case: "Template - override multiple",
|
||||
Expected: "color3",
|
||||
Default: "color",
|
||||
Templates: []string{
|
||||
"{{if contains \"doe\" .Profile}}color2{{end}}",
|
||||
"{{if contains \"john\" .Profile}}color3{{end}}",
|
||||
|
@ -131,9 +133,9 @@ func TestGetColors(t *testing.T) {
|
|||
Profile: "john",
|
||||
},
|
||||
{
|
||||
Case: "Template - override multiple no match",
|
||||
ExpectedColor: "color",
|
||||
DefaultColor: "color",
|
||||
Case: "Template - override multiple no match",
|
||||
Expected: "color",
|
||||
Default: "color",
|
||||
Templates: []string{
|
||||
"{{if contains \"doe\" .Profile}}color2{{end}}",
|
||||
"{{if contains \"philip\" .Profile}}color3{{end}}",
|
||||
|
@ -142,11 +144,13 @@ func TestGetColors(t *testing.T) {
|
|||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
env := new(mock.MockedEnvironment)
|
||||
env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
|
||||
env.On("TemplateCache").Return(&platform.TemplateCache{
|
||||
env := new(mock.Environment)
|
||||
env.On("DebugF", testify_.Anything, testify_.Anything).Return(nil)
|
||||
env.On("TemplateCache").Return(&cache.Template{
|
||||
Env: make(map[string]string),
|
||||
})
|
||||
env.On("Flags").Return(&runtime.Flags{})
|
||||
|
||||
segment := &Segment{
|
||||
writer: &segments.Aws{
|
||||
Profile: tc.Profile,
|
||||
|
@ -154,16 +158,18 @@ func TestGetColors(t *testing.T) {
|
|||
},
|
||||
env: env,
|
||||
}
|
||||
|
||||
if tc.Background {
|
||||
segment.Background = tc.DefaultColor
|
||||
segment.Background = tc.Default
|
||||
segment.BackgroundTemplates = tc.Templates
|
||||
color := segment.background()
|
||||
assert.Equal(t, tc.ExpectedColor, color, tc.Case)
|
||||
bgColor := segment.ResolveBackground()
|
||||
assert.Equal(t, tc.Expected, bgColor, tc.Case)
|
||||
continue
|
||||
}
|
||||
segment.Foreground = tc.DefaultColor
|
||||
|
||||
segment.Foreground = tc.Default
|
||||
segment.ForegroundTemplates = tc.Templates
|
||||
color := segment.foreground()
|
||||
assert.Equal(t, tc.ExpectedColor, color, tc.Case)
|
||||
fgColor := segment.ResolveForeground()
|
||||
assert.Equal(t, tc.Expected, fgColor, tc.Case)
|
||||
}
|
||||
}
|
|
@ -1,88 +1,23 @@
|
|||
package engine
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/segments"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
|
||||
c "golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Segment represent a single segment and it's configuration
|
||||
type Segment struct {
|
||||
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:"-" toml:"-"`
|
||||
|
||||
colors *ansi.Colors
|
||||
env platform.Environment
|
||||
writer SegmentWriter
|
||||
text string
|
||||
styleCache SegmentStyle
|
||||
name string
|
||||
|
||||
// debug info
|
||||
duration time.Duration
|
||||
nameLength int
|
||||
}
|
||||
// SegmentType the type of segment, for more information, see the constants
|
||||
type SegmentType string
|
||||
|
||||
// SegmentWriter is the interface used to define what and if to write to the prompt
|
||||
type SegmentWriter interface {
|
||||
Enabled() bool
|
||||
Template() string
|
||||
Init(props properties.Properties, env platform.Environment)
|
||||
Init(props properties.Properties, env runtime.Environment)
|
||||
}
|
||||
|
||||
// SegmentStyle the style of segment, for more information, see the constants
|
||||
type SegmentStyle string
|
||||
|
||||
func (s *SegmentStyle) Resolve(env platform.Environment, context any) SegmentStyle {
|
||||
txtTemplate := &template.Text{
|
||||
Context: context,
|
||||
Env: env,
|
||||
}
|
||||
txtTemplate.Template = string(*s)
|
||||
value, err := txtTemplate.Render()
|
||||
// default to Plain
|
||||
if err != nil || len(value) == 0 {
|
||||
return Plain
|
||||
}
|
||||
return SegmentStyle(value)
|
||||
}
|
||||
|
||||
// SegmentType the type of segment, for more information, see the constants
|
||||
type SegmentType string
|
||||
|
||||
const (
|
||||
// Plain writes it without ornaments
|
||||
Plain SegmentStyle = "plain"
|
||||
|
@ -92,7 +27,6 @@ const (
|
|||
Accordion SegmentStyle = "accordion"
|
||||
// Diamond writes the prompt shaped with a leading and trailing symbol
|
||||
Diamond SegmentStyle = "diamond"
|
||||
|
||||
// ANGULAR writes which angular cli version us currently active
|
||||
ANGULAR SegmentType = "angular"
|
||||
// ARGOCD writes the current argocd context
|
||||
|
@ -101,6 +35,8 @@ const (
|
|||
AWS SegmentType = "aws"
|
||||
// AZ writes the Azure subscription info we're currently in
|
||||
AZ SegmentType = "az"
|
||||
// AZD writes the Azure Developer CLI environment info we're current in
|
||||
AZD SegmentType = "azd"
|
||||
// AZFUNC writes current AZ func version
|
||||
AZFUNC SegmentType = "azfunc"
|
||||
// BATTERY writes the battery percentage
|
||||
|
@ -111,6 +47,8 @@ const (
|
|||
BREWFATHER SegmentType = "brewfather"
|
||||
// Buf segment writes the active buf version
|
||||
BUF SegmentType = "buf"
|
||||
// BUN writes the active bun version
|
||||
BUN SegmentType = "bun"
|
||||
// CARBONINTENSITY writes the actual and forecast carbon intensity in gCO2/kWh
|
||||
CARBONINTENSITY SegmentType = "carbonintensity"
|
||||
// cds (SAP CAP) version
|
||||
|
@ -175,6 +113,8 @@ const (
|
|||
LUA SegmentType = "lua"
|
||||
// MERCURIAL writes the Mercurial source control information
|
||||
MERCURIAL SegmentType = "mercurial"
|
||||
// MVN writes the active maven version
|
||||
MVN SegmentType = "mvn"
|
||||
// NBA writes NBA game data
|
||||
NBA SegmentType = "nba"
|
||||
// NBGV writes the nbgv version information
|
||||
|
@ -187,6 +127,8 @@ const (
|
|||
NPM SegmentType = "npm"
|
||||
// NX writes which Nx version us currently active
|
||||
NX SegmentType = "nx"
|
||||
// NIXSHELL writes the active nix shell details
|
||||
NIXSHELL SegmentType = "nix-shell"
|
||||
// OCAML writes the active Ocaml version
|
||||
OCAML SegmentType = "ocaml"
|
||||
// OS write os specific icon
|
||||
|
@ -201,6 +143,8 @@ const (
|
|||
PHP SegmentType = "php"
|
||||
// PLASTIC represents the plastic scm status and information
|
||||
PLASTIC SegmentType = "plastic"
|
||||
// pnpm version
|
||||
PNPM SegmentType = "pnpm"
|
||||
// Project version
|
||||
PROJECT SegmentType = "project"
|
||||
// PULUMI writes the pulumi user, store and stack
|
||||
|
@ -265,6 +209,8 @@ const (
|
|||
WITHINGS SegmentType = "withings"
|
||||
// XMAKE write the xmake version if xmake.lua is present
|
||||
XMAKE SegmentType = "xmake"
|
||||
// yarn version
|
||||
YARN SegmentType = "yarn"
|
||||
// YTM writes YouTube Music information and status
|
||||
YTM SegmentType = "ytm"
|
||||
)
|
||||
|
@ -276,11 +222,13 @@ var Segments = map[SegmentType]func() SegmentWriter{
|
|||
ARGOCD: func() SegmentWriter { return &segments.Argocd{} },
|
||||
AWS: func() SegmentWriter { return &segments.Aws{} },
|
||||
AZ: func() SegmentWriter { return &segments.Az{} },
|
||||
AZD: func() SegmentWriter { return &segments.Azd{} },
|
||||
AZFUNC: func() SegmentWriter { return &segments.AzFunc{} },
|
||||
BATTERY: func() SegmentWriter { return &segments.Battery{} },
|
||||
BAZEL: func() SegmentWriter { return &segments.Bazel{} },
|
||||
BREWFATHER: func() SegmentWriter { return &segments.Brewfather{} },
|
||||
BUF: func() SegmentWriter { return &segments.Buf{} },
|
||||
BUN: func() SegmentWriter { return &segments.Bun{} },
|
||||
CARBONINTENSITY: func() SegmentWriter { return &segments.CarbonIntensity{} },
|
||||
CDS: func() SegmentWriter { return &segments.Cds{} },
|
||||
CF: func() SegmentWriter { return &segments.Cf{} },
|
||||
|
@ -313,11 +261,13 @@ var Segments = map[SegmentType]func() SegmentWriter{
|
|||
LASTFM: func() SegmentWriter { return &segments.LastFM{} },
|
||||
LUA: func() SegmentWriter { return &segments.Lua{} },
|
||||
MERCURIAL: func() SegmentWriter { return &segments.Mercurial{} },
|
||||
MVN: func() SegmentWriter { return &segments.Mvn{} },
|
||||
NBA: func() SegmentWriter { return &segments.Nba{} },
|
||||
NBGV: func() SegmentWriter { return &segments.Nbgv{} },
|
||||
NIGHTSCOUT: func() SegmentWriter { return &segments.Nightscout{} },
|
||||
NODE: func() SegmentWriter { return &segments.Node{} },
|
||||
NPM: func() SegmentWriter { return &segments.Npm{} },
|
||||
NIXSHELL: func() SegmentWriter { return &segments.NixShell{} },
|
||||
NX: func() SegmentWriter { return &segments.Nx{} },
|
||||
OCAML: func() SegmentWriter { return &segments.OCaml{} },
|
||||
OS: func() SegmentWriter { return &segments.Os{} },
|
||||
|
@ -326,6 +276,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
|
|||
PERL: func() SegmentWriter { return &segments.Perl{} },
|
||||
PHP: func() SegmentWriter { return &segments.Php{} },
|
||||
PLASTIC: func() SegmentWriter { return &segments.Plastic{} },
|
||||
PNPM: func() SegmentWriter { return &segments.Pnpm{} },
|
||||
PROJECT: func() SegmentWriter { return &segments.Project{} },
|
||||
PULUMI: func() SegmentWriter { return &segments.Pulumi{} },
|
||||
PYTHON: func() SegmentWriter { return &segments.Python{} },
|
||||
|
@ -358,95 +309,11 @@ var Segments = map[SegmentType]func() SegmentWriter{
|
|||
WINREG: func() SegmentWriter { return &segments.WindowsRegistry{} },
|
||||
WITHINGS: func() SegmentWriter { return &segments.Withings{} },
|
||||
XMAKE: func() SegmentWriter { return &segments.XMake{} },
|
||||
YARN: func() SegmentWriter { return &segments.Yarn{} },
|
||||
YTM: func() SegmentWriter { return &segments.Ytm{} },
|
||||
}
|
||||
|
||||
func (segment *Segment) style() SegmentStyle {
|
||||
if len(segment.styleCache) != 0 {
|
||||
return segment.styleCache
|
||||
}
|
||||
segment.styleCache = segment.Style.Resolve(segment.env, segment.writer)
|
||||
return segment.styleCache
|
||||
}
|
||||
|
||||
func (segment *Segment) shouldIncludeFolder() bool {
|
||||
if segment.env == nil {
|
||||
return true
|
||||
}
|
||||
cwdIncluded := segment.cwdIncluded()
|
||||
cwdExcluded := segment.cwdExcluded()
|
||||
return cwdIncluded && !cwdExcluded
|
||||
}
|
||||
|
||||
func (segment *Segment) isPowerline() bool {
|
||||
style := segment.style()
|
||||
return style == Powerline || style == Accordion
|
||||
}
|
||||
|
||||
func (segment *Segment) hasEmptyDiamondAtEnd() bool {
|
||||
if segment.style() != Diamond {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(segment.TrailingDiamond) == 0
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdIncluded() bool {
|
||||
value, ok := segment.Properties[properties.IncludeFolders]
|
||||
if !ok {
|
||||
// IncludeFolders isn't specified, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
list := properties.ParseStringArray(value)
|
||||
|
||||
if len(list) == 0 {
|
||||
// IncludeFolders is an empty array, everything is included
|
||||
return true
|
||||
}
|
||||
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) cwdExcluded() bool {
|
||||
value, ok := segment.Properties[properties.ExcludeFolders]
|
||||
if !ok {
|
||||
value = segment.Properties[properties.IgnoreFolders]
|
||||
}
|
||||
list := properties.ParseStringArray(value)
|
||||
return segment.env.DirMatchesOneOf(segment.env.Pwd(), list)
|
||||
}
|
||||
|
||||
func (segment *Segment) shouldInvokeWithTip(tip string) bool {
|
||||
for _, t := range segment.Tips {
|
||||
if t == tip {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (segment *Segment) foreground() string {
|
||||
if segment.colors == nil {
|
||||
segment.colors = &ansi.Colors{}
|
||||
}
|
||||
if len(segment.colors.Foreground) == 0 {
|
||||
segment.colors.Foreground = segment.ForegroundTemplates.FirstMatch(segment.writer, segment.env, segment.Foreground)
|
||||
}
|
||||
return segment.colors.Foreground
|
||||
}
|
||||
|
||||
func (segment *Segment) background() string {
|
||||
if segment.colors == nil {
|
||||
segment.colors = &ansi.Colors{}
|
||||
}
|
||||
if len(segment.colors.Background) == 0 {
|
||||
segment.colors.Background = segment.BackgroundTemplates.FirstMatch(segment.writer, segment.env, segment.Background)
|
||||
}
|
||||
return segment.colors.Background
|
||||
}
|
||||
|
||||
func (segment *Segment) mapSegmentWithWriter(env platform.Environment) error {
|
||||
func (segment *Segment) MapSegmentWithWriter(env runtime.Environment) error {
|
||||
segment.env = env
|
||||
|
||||
if segment.Properties == nil {
|
||||
|
@ -457,7 +324,6 @@ func (segment *Segment) mapSegmentWithWriter(env platform.Environment) error {
|
|||
writer := f()
|
||||
wrapper := &properties.Wrapper{
|
||||
Properties: segment.Properties,
|
||||
Env: env,
|
||||
}
|
||||
writer.Init(wrapper, env)
|
||||
segment.writer = writer
|
||||
|
@ -466,110 +332,3 @@ func (segment *Segment) mapSegmentWithWriter(env platform.Environment) error {
|
|||
|
||||
return errors.New("unable to map writer")
|
||||
}
|
||||
|
||||
func (segment *Segment) string() string {
|
||||
var templatesResult string
|
||||
if !segment.Templates.Empty() {
|
||||
templatesResult = segment.Templates.Resolve(segment.writer, segment.env, "", segment.TemplatesLogic)
|
||||
if len(segment.Template) == 0 {
|
||||
return templatesResult
|
||||
}
|
||||
}
|
||||
if len(segment.Template) == 0 {
|
||||
segment.Template = segment.writer.Template()
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: segment.Template,
|
||||
Context: segment.writer,
|
||||
Env: segment.env,
|
||||
TemplatesResult: templatesResult,
|
||||
}
|
||||
text, err := tmpl.Render()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func (segment *Segment) Name() string {
|
||||
if len(segment.name) != 0 {
|
||||
return segment.name
|
||||
}
|
||||
name := segment.Alias
|
||||
if len(name) == 0 {
|
||||
name = c.Title(language.English).String(string(segment.Type))
|
||||
}
|
||||
segment.name = name
|
||||
return name
|
||||
}
|
||||
|
||||
func (segment *Segment) SetEnabled(env platform.Environment) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
// display a message explaining omp failed(with the err)
|
||||
message := fmt.Sprintf("\noh-my-posh fatal error rendering %s segment:%s\n\n%s\n", segment.Type, err, debug.Stack())
|
||||
fmt.Println(message)
|
||||
segment.Enabled = true
|
||||
}()
|
||||
|
||||
// segment timings for debug purposes
|
||||
var start time.Time
|
||||
if env.Flags().Debug {
|
||||
start = time.Now()
|
||||
segment.nameLength = len(segment.Name())
|
||||
defer func() {
|
||||
segment.duration = time.Since(start)
|
||||
}()
|
||||
}
|
||||
|
||||
err := segment.mapSegmentWithWriter(env)
|
||||
if err != nil || !segment.shouldIncludeFolder() {
|
||||
return
|
||||
}
|
||||
|
||||
segment.env.DebugF("Segment: %s", segment.Name())
|
||||
|
||||
// validate toggles
|
||||
if toggles, OK := segment.env.Cache().Get(platform.TOGGLECACHE); OK && len(toggles) > 0 {
|
||||
list := strings.Split(toggles, ",")
|
||||
for _, toggle := range list {
|
||||
if SegmentType(toggle) == segment.Type || toggle == segment.Alias {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shouldHideForWidth(segment.env, segment.MinWidth, segment.MaxWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
if segment.writer.Enabled() {
|
||||
segment.Enabled = true
|
||||
env.TemplateCache().AddSegmentData(segment.Name(), segment.writer)
|
||||
}
|
||||
}
|
||||
|
||||
func (segment *Segment) SetText() {
|
||||
if !segment.Enabled {
|
||||
return
|
||||
}
|
||||
segment.text = segment.string()
|
||||
segment.Enabled = len(strings.ReplaceAll(segment.text, " ", "")) > 0
|
||||
if !segment.Enabled {
|
||||
segment.env.TemplateCache().RemoveSegmentData(segment.Name())
|
||||
}
|
||||
|
||||
if segment.Interactive {
|
||||
return
|
||||
}
|
||||
// we have to do this to prevent bash/zsh from misidentifying escape sequences
|
||||
switch segment.env.Shell() {
|
||||
case shell.BASH:
|
||||
segment.text = strings.NewReplacer("`", "\\`", `\`, `\\`).Replace(segment.text)
|
||||
case shell.ZSH:
|
||||
segment.text = strings.NewReplacer("`", "\\`", `%`, `%%`).Replace(segment.text)
|
||||
}
|
||||
}
|
|
@ -1,304 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/regex"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
)
|
||||
|
||||
// BlockType type of block
|
||||
type BlockType string
|
||||
|
||||
// BlockAlignment aligment of a Block
|
||||
type BlockAlignment string
|
||||
|
||||
// Overflow defines how to handle a right block that overflows with the previous block
|
||||
type Overflow string
|
||||
|
||||
const (
|
||||
// Prompt writes one or more Segments
|
||||
Prompt BlockType = "prompt"
|
||||
// LineBreak creates a line break in the prompt
|
||||
LineBreak BlockType = "newline"
|
||||
// RPrompt is a right aligned prompt
|
||||
RPrompt BlockType = "rprompt"
|
||||
// Left aligns left
|
||||
Left BlockAlignment = "left"
|
||||
// Right aligns right
|
||||
Right BlockAlignment = "right"
|
||||
// Break adds a line break
|
||||
Break Overflow = "break"
|
||||
// Hide hides the block
|
||||
Hide Overflow = "hide"
|
||||
)
|
||||
|
||||
// Block defines a part of the prompt with optional segments
|
||||
type Block struct {
|
||||
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" toml:"horizontal_offset,omitempty"`
|
||||
VerticalOffset int `json:"vertical_offset,omitempty" toml:"vertical_offset,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
|
||||
activeSegment *Segment
|
||||
previousActiveSegment *Segment
|
||||
}
|
||||
|
||||
func (b *Block) Init(env platform.Environment, writer *ansi.Writer) {
|
||||
b.env = env
|
||||
b.writer = writer
|
||||
b.executeSegmentLogic()
|
||||
}
|
||||
|
||||
func (b *Block) InitPlain(env platform.Environment, config *Config) {
|
||||
b.writer = &ansi.Writer{
|
||||
TerminalBackground: shell.ConsoleBackgroundColor(env, config.TerminalBackground),
|
||||
AnsiColors: config.MakeColors(),
|
||||
TrueColor: env.Flags().TrueColor,
|
||||
}
|
||||
b.writer.Init(shell.GENERIC)
|
||||
b.env = env
|
||||
b.executeSegmentLogic()
|
||||
}
|
||||
|
||||
func (b *Block) executeSegmentLogic() {
|
||||
if shouldHideForWidth(b.env, b.MinWidth, b.MaxWidth) {
|
||||
return
|
||||
}
|
||||
b.setEnabledSegments()
|
||||
b.setSegmentsText()
|
||||
}
|
||||
|
||||
func (b *Block) setActiveSegment(segment *Segment) {
|
||||
b.activeSegment = segment
|
||||
b.writer.SetColors(segment.background(), segment.foreground())
|
||||
}
|
||||
|
||||
func (b *Block) Enabled() bool {
|
||||
if b.Type == LineBreak {
|
||||
return true
|
||||
}
|
||||
for _, segment := range b.Segments {
|
||||
if segment.Enabled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Block) setEnabledSegments() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.SetEnabled(b.env)
|
||||
}(segment)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) setSegmentsText() {
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(b.Segments))
|
||||
defer wg.Wait()
|
||||
for _, segment := range b.Segments {
|
||||
go func(s *Segment) {
|
||||
defer wg.Done()
|
||||
s.SetText()
|
||||
}(segment)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) RenderSegments() (string, int) {
|
||||
for _, segment := range b.Segments {
|
||||
if !segment.Enabled && segment.style() != Accordion {
|
||||
continue
|
||||
}
|
||||
|
||||
if colors, newCycle := cycle.Loop(); colors != nil {
|
||||
cycle = &newCycle
|
||||
segment.colors = colors
|
||||
}
|
||||
|
||||
b.setActiveSegment(segment)
|
||||
b.renderActiveSegment()
|
||||
}
|
||||
|
||||
b.writeSeparator(true)
|
||||
|
||||
return b.writer.String()
|
||||
}
|
||||
|
||||
func (b *Block) renderActiveSegment() {
|
||||
b.writeSeparator(false)
|
||||
switch b.activeSegment.style() {
|
||||
case Plain, Powerline:
|
||||
b.writer.Write(ansi.Background, ansi.Foreground, b.activeSegment.text)
|
||||
case Diamond:
|
||||
background := ansi.Transparent
|
||||
if b.previousActiveSegment != nil && b.previousActiveSegment.hasEmptyDiamondAtEnd() {
|
||||
background = b.previousActiveSegment.background()
|
||||
}
|
||||
b.writer.Write(background, ansi.Background, b.activeSegment.LeadingDiamond)
|
||||
b.writer.Write(ansi.Background, ansi.Foreground, b.activeSegment.text)
|
||||
case Accordion:
|
||||
if b.activeSegment.Enabled {
|
||||
b.writer.Write(ansi.Background, ansi.Foreground, b.activeSegment.text)
|
||||
}
|
||||
}
|
||||
b.previousActiveSegment = b.activeSegment
|
||||
b.writer.SetParentColors(b.previousActiveSegment.background(), b.previousActiveSegment.foreground())
|
||||
}
|
||||
|
||||
func (b *Block) writeSeparator(final bool) {
|
||||
isCurrentDiamond := b.activeSegment.style() == Diamond
|
||||
if final && isCurrentDiamond {
|
||||
b.writer.Write(ansi.Transparent, ansi.Background, b.activeSegment.TrailingDiamond)
|
||||
return
|
||||
}
|
||||
|
||||
isPreviousDiamond := b.previousActiveSegment != nil && b.previousActiveSegment.style() == Diamond
|
||||
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)
|
||||
}
|
||||
|
||||
isPowerline := b.activeSegment.isPowerline()
|
||||
|
||||
shouldOverridePowerlineLeadingSymbol := func() bool {
|
||||
if !isPowerline {
|
||||
return false
|
||||
}
|
||||
|
||||
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 || !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)
|
||||
}
|
||||
|
||||
func (b *Block) adjustTrailingDiamondColorOverrides() {
|
||||
// as we now already adjusted the activeSegment, we need to change the value
|
||||
// of background and foreground to parentBackground and parentForeground
|
||||
// this will still break when using parentBackground and parentForeground as keywords
|
||||
// in a trailing diamond, but let's fix that when it happens as it requires either a rewrite
|
||||
// of the logic for diamonds or storing grandparents as well like one happy family.
|
||||
if b.previousActiveSegment == nil || len(b.previousActiveSegment.TrailingDiamond) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.Contains(b.previousActiveSegment.TrailingDiamond, ansi.Background) && !strings.Contains(b.previousActiveSegment.TrailingDiamond, ansi.Foreground) {
|
||||
return
|
||||
}
|
||||
|
||||
match := regex.FindNamedRegexMatch(ansi.AnchorRegex, b.previousActiveSegment.TrailingDiamond)
|
||||
if len(match) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
adjustOverride := func(anchor, override string) {
|
||||
newOverride := override
|
||||
switch override {
|
||||
case ansi.Foreground:
|
||||
newOverride = ansi.ParentForeground
|
||||
case ansi.Background:
|
||||
newOverride = ansi.ParentBackground
|
||||
}
|
||||
|
||||
if override == newOverride {
|
||||
return
|
||||
}
|
||||
|
||||
newAnchor := strings.Replace(match[ansi.ANCHOR], override, newOverride, 1)
|
||||
b.previousActiveSegment.TrailingDiamond = strings.Replace(b.previousActiveSegment.TrailingDiamond, anchor, newAnchor, 1)
|
||||
}
|
||||
|
||||
if len(match[ansi.BG]) > 0 {
|
||||
adjustOverride(match[ansi.ANCHOR], match[ansi.BG])
|
||||
}
|
||||
if len(match[ansi.FG]) > 0 {
|
||||
adjustOverride(match[ansi.ANCHOR], match[ansi.FG])
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) getPowerlineColor() string {
|
||||
if b.previousActiveSegment == nil {
|
||||
return ansi.Transparent
|
||||
}
|
||||
if b.previousActiveSegment.style() == Diamond && len(b.previousActiveSegment.TrailingDiamond) == 0 {
|
||||
return b.previousActiveSegment.background()
|
||||
}
|
||||
if b.activeSegment.style() == Diamond && len(b.activeSegment.LeadingDiamond) == 0 {
|
||||
return b.previousActiveSegment.background()
|
||||
}
|
||||
if !b.previousActiveSegment.isPowerline() {
|
||||
return ansi.Transparent
|
||||
}
|
||||
return b.previousActiveSegment.background()
|
||||
}
|
|
@ -1,524 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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"
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
JSON string = "json"
|
||||
YAML string = "yaml"
|
||||
TOML string = "toml"
|
||||
|
||||
configVersion = 2
|
||||
)
|
||||
|
||||
// Config holds all the theme for rendering the prompt
|
||||
type Config struct {
|
||||
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" toml:"osc99,omitempty"`
|
||||
|
||||
Output string `json:"-" toml:"-"`
|
||||
MigrateGlyphs bool `json:"-" toml:"-"`
|
||||
Format string `json:"-" toml:"-"`
|
||||
|
||||
origin string
|
||||
// eval bool
|
||||
updated bool
|
||||
env platform.Environment
|
||||
}
|
||||
|
||||
// MakeColors creates instance of AnsiColors to use in AnsiWriter according to
|
||||
// environment and configuration.
|
||||
func (cfg *Config) MakeColors() ansi.ColorString {
|
||||
cacheDisabled := cfg.env.Getenv("OMP_CACHE_DISABLED") == "1"
|
||||
return ansi.MakeColors(cfg.getPalette(), !cacheDisabled, cfg.AccentColor, cfg.env)
|
||||
}
|
||||
|
||||
func (cfg *Config) getPalette() ansi.Palette {
|
||||
if cfg.Palettes == nil {
|
||||
return cfg.Palette
|
||||
}
|
||||
tmpl := &template.Text{
|
||||
Template: cfg.Palettes.Template,
|
||||
Env: cfg.env,
|
||||
}
|
||||
if palette, err := tmpl.Render(); err == nil {
|
||||
if p, ok := cfg.Palettes.List[palette]; ok {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return cfg.Palette
|
||||
}
|
||||
|
||||
// LoadConfig returns the default configuration including possible user overrides
|
||||
func LoadConfig(env platform.Environment) *Config {
|
||||
cfg := loadConfig(env)
|
||||
|
||||
// only migrate automatically when the switch isn't set
|
||||
if !env.Flags().Migrate && cfg.Version < configVersion {
|
||||
cfg.BackupAndMigrate()
|
||||
}
|
||||
|
||||
if !cfg.ShellIntegration {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// bash - ok
|
||||
// fish - ok
|
||||
// pwsh - ok
|
||||
// zsh - ok
|
||||
// cmd - ok, as of v1.4.25 (chrisant996/clink#457, fixed in chrisant996/clink@8a5d7ea)
|
||||
// nu - built-in (and bugged) feature - nushell/nushell#5585, https://www.nushell.sh/blog/2022-08-16-nushell-0_67.html#shell-integration-fdncred-and-tyriar
|
||||
// elv - broken OSC sequences
|
||||
// xonsh - broken OSC sequences
|
||||
// tcsh - overall broken, FTCS_COMMAND_EXECUTED could be added to POSH_POSTCMD in the future
|
||||
switch env.Shell() {
|
||||
case shell.ELVISH, shell.XONSH, shell.TCSH, shell.NU:
|
||||
cfg.ShellIntegration = false
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func loadConfig(env platform.Environment) *Config {
|
||||
defer env.Trace(time.Now())
|
||||
configFile := env.Flags().Config
|
||||
|
||||
if len(configFile) == 0 {
|
||||
env.Debug("no config file specified, using default")
|
||||
return defaultConfig(env, false)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
cfg.origin = configFile
|
||||
cfg.Format = strings.TrimPrefix(filepath.Ext(configFile), ".")
|
||||
cfg.env = env
|
||||
|
||||
data, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
env.DebugF("error reading config file: %s", err)
|
||||
return defaultConfig(env, true)
|
||||
}
|
||||
|
||||
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.DebugF("error decoding config file: %s", err)
|
||||
return defaultConfig(env, true)
|
||||
}
|
||||
|
||||
return &cfg
|
||||
}
|
||||
|
||||
func (cfg *Config) Export(format string) string {
|
||||
if len(format) != 0 {
|
||||
cfg.Format = format
|
||||
}
|
||||
|
||||
var result bytes.Buffer
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// unsupported format
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cfg *Config) BackupAndMigrate() {
|
||||
cfg.Backup()
|
||||
cfg.Migrate()
|
||||
cfg.Write(cfg.Format)
|
||||
}
|
||||
|
||||
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, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
|
||||
_, err = f.WriteString(content)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) Backup() {
|
||||
dst := cfg.origin + ".bak"
|
||||
source, err := os.Open(cfg.origin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer source.Close()
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer destination.Close()
|
||||
_, err = io.Copy(destination, source)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func escapeGlyphs(s string, migrate bool) string {
|
||||
shouldExclude := func(r rune) bool {
|
||||
if r < 0x1000 { // Basic Multilingual Plane
|
||||
return true
|
||||
}
|
||||
if r > 0x1F600 && r < 0x1F64F { // Emoticons
|
||||
return true
|
||||
}
|
||||
if r > 0x1F300 && r < 0x1F5FF { // Misc Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F680 && r < 0x1F6FF { // Transport and Map
|
||||
return true
|
||||
}
|
||||
if r > 0x2600 && r < 0x26FF { // Misc symbols
|
||||
return true
|
||||
}
|
||||
if r > 0x2700 && r < 0x27BF { // Dingbats
|
||||
return true
|
||||
}
|
||||
if r > 0xFE00 && r < 0xFE0F { // Variation Selectors
|
||||
return true
|
||||
}
|
||||
if r > 0x1F900 && r < 0x1F9FF { // Supplemental Symbols and Pictographs
|
||||
return true
|
||||
}
|
||||
if r > 0x1F1E6 && r < 0x1F1FF { // Flags
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var cp codePoints
|
||||
var err error
|
||||
if migrate {
|
||||
cp, err = getGlyphCodePoints()
|
||||
if err != nil {
|
||||
migrate = false
|
||||
}
|
||||
}
|
||||
|
||||
var builder strings.Builder
|
||||
for _, r := range s {
|
||||
// exclude regular characters and emojis
|
||||
if shouldExclude(r) {
|
||||
builder.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
if migrate {
|
||||
if val, OK := cp[uint64(r)]; OK {
|
||||
r = rune(val)
|
||||
}
|
||||
}
|
||||
|
||||
if r > 0x10000 {
|
||||
// calculate surrogate pairs
|
||||
one := 0xd800 + (((r - 0x10000) >> 10) & 0x3ff)
|
||||
two := 0xdc00 + ((r - 0x10000) & 0x3ff)
|
||||
quoted := fmt.Sprintf("\\u%04x\\u%04x", one, two)
|
||||
builder.WriteString(quoted)
|
||||
continue
|
||||
}
|
||||
|
||||
quoted := fmt.Sprintf("\\u%04x", r)
|
||||
builder.WriteString(quoted)
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func defaultConfig(env platform.Environment, warning bool) *Config {
|
||||
exitBackgroundTemplate := "{{ if gt .Code 0 }}p:red{{ end }}"
|
||||
exitTemplate := " {{ if gt .Code 0 }}\uf00d{{ else }}\uf00c{{ end }} "
|
||||
if warning {
|
||||
exitBackgroundTemplate = "p:red"
|
||||
exitTemplate = " CONFIG ERROR "
|
||||
}
|
||||
cfg := &Config{
|
||||
Version: 2,
|
||||
FinalSpace: true,
|
||||
Blocks: []*Block{
|
||||
{
|
||||
Type: Prompt,
|
||||
Alignment: Left,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: SESSION,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b6",
|
||||
TrailingDiamond: "\ue0b0",
|
||||
Background: "p:yellow",
|
||||
Foreground: "p:black",
|
||||
Template: " {{ if .SSHSession }}\ueba9 {{ end }}{{ .UserName }} ",
|
||||
},
|
||||
{
|
||||
Type: PATH,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:orange",
|
||||
Foreground: "p:white",
|
||||
Properties: properties.Map{
|
||||
properties.Style: "folder",
|
||||
},
|
||||
Template: " \uea83 {{ path .Path .Location }} ",
|
||||
},
|
||||
{
|
||||
Type: GIT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:green",
|
||||
BackgroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:yellow{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:red{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}#49416D{{ end }}",
|
||||
"{{ if gt .Behind 0 }}#7A306C{{ end }}",
|
||||
},
|
||||
Foreground: "p:black",
|
||||
ForegroundTemplates: []string{
|
||||
"{{ if or (.Working.Changed) (.Staging.Changed) }}p:black{{ end }}",
|
||||
"{{ if and (gt .Ahead 0) (gt .Behind 0) }}p:white{{ end }}",
|
||||
"{{ if gt .Ahead 0 }}p:white{{ end }}",
|
||||
},
|
||||
Properties: properties.Map{
|
||||
segments.BranchMaxLength: 25,
|
||||
segments.FetchStatus: true,
|
||||
segments.FetchUpstreamIcon: true,
|
||||
},
|
||||
Template: " {{ if .UpstreamURL }}{{ url .UpstreamIcon .UpstreamURL }} {{ end }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} \uf044 {{ .Working.String }}{{ end }}{{ if .Staging.Changed }} \uf046 {{ .Staging.String }}{{ end }} ", //nolint:lll
|
||||
},
|
||||
{
|
||||
Type: ROOT,
|
||||
Style: Powerline,
|
||||
PowerlineSymbol: "\ue0b0",
|
||||
Background: "p:yellow",
|
||||
Foreground: "p:white",
|
||||
Template: " \uf0e7 ",
|
||||
},
|
||||
{
|
||||
Type: STATUS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "<transparent,background>\ue0b0</>",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:blue",
|
||||
BackgroundTemplates: []string{
|
||||
exitBackgroundTemplate,
|
||||
},
|
||||
Foreground: "p:white",
|
||||
Properties: properties.Map{
|
||||
properties.AlwaysEnabled: true,
|
||||
},
|
||||
Template: exitTemplate,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: RPrompt,
|
||||
Segments: []*Segment{
|
||||
{
|
||||
Type: NODE,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:green",
|
||||
Template: "\ue718 ",
|
||||
Properties: properties.Map{
|
||||
segments.HomeEnabled: false,
|
||||
segments.FetchPackageManager: false,
|
||||
segments.DisplayMode: "files",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: GOLANG,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:blue",
|
||||
Template: "\ue626 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: PYTHON,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:yellow",
|
||||
Template: "\ue235 ",
|
||||
Properties: properties.Map{
|
||||
properties.FetchVersion: false,
|
||||
segments.DisplayMode: "files",
|
||||
segments.FetchVirtualEnv: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: SHELL,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:white",
|
||||
Template: "in <p:blue><b>{{ .Name }}</b></> ",
|
||||
},
|
||||
{
|
||||
Type: TIME,
|
||||
Style: Plain,
|
||||
Background: "transparent",
|
||||
Foreground: "p:white",
|
||||
Template: "at <p:blue><b>{{ .CurrentDate | date \"15:04:05\" }}</b></>",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ConsoleTitleTemplate: "{{ .Shell }} in {{ .Folder }}",
|
||||
Palette: ansi.Palette{
|
||||
"black": "#262B44",
|
||||
"blue": "#4B95E9",
|
||||
"green": "#59C9A5",
|
||||
"orange": "#F07623",
|
||||
"red": "#D81E5B",
|
||||
"white": "#E0DEF4",
|
||||
"yellow": "#F3AE35",
|
||||
},
|
||||
SecondaryPrompt: &Segment{
|
||||
Background: "transparent",
|
||||
Foreground: "p:black",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> > </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
TransientPrompt: &Segment{
|
||||
Background: "transparent",
|
||||
Foreground: "p:black",
|
||||
Template: "<p:yellow,transparent>\ue0b6</><,p:yellow> {{ .Folder }} </><p:yellow,transparent>\ue0b0</> ",
|
||||
},
|
||||
Tooltips: []*Segment{
|
||||
{
|
||||
Type: AWS,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:orange",
|
||||
Foreground: "p:white",
|
||||
Template: " \ue7ad {{ .Profile }}{{ if .Region }}@{{ .Region }}{{ end }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"aws"},
|
||||
},
|
||||
{
|
||||
Type: AZ,
|
||||
Style: Diamond,
|
||||
LeadingDiamond: "\ue0b0",
|
||||
TrailingDiamond: "\ue0b4",
|
||||
Background: "p:blue",
|
||||
Foreground: "p:white",
|
||||
Template: " \uebd8 {{ .Name }} ",
|
||||
Properties: properties.Map{
|
||||
properties.DisplayDefault: true,
|
||||
},
|
||||
Tips: []string{"az"},
|
||||
},
|
||||
},
|
||||
}
|
||||
cfg.env = env
|
||||
return cfg
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"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/stretchr/testify/assert"
|
||||
mock2 "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestEscapeGlyphs(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Expected string
|
||||
}{
|
||||
{Input: "", Expected: "\\udb80\\ude4b"},
|
||||
{Input: "a", Expected: "a"},
|
||||
{Input: "\ue0b4", Expected: "\\ue0b4"},
|
||||
{Input: "\ufd03", Expected: "\\ufd03"},
|
||||
{Input: "}", Expected: "}"},
|
||||
{Input: "🏚", Expected: "🏚"},
|
||||
{Input: "\U000F011B", Expected: "\\udb80\\udd1b"},
|
||||
{Input: "", Expected: "\\udb80\\udd1b"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
assert.Equal(t, tc.Expected, escapeGlyphs(tc.Input, false), tc.Input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPalette(t *testing.T) {
|
||||
palette := ansi.Palette{
|
||||
"red": "#ff0000",
|
||||
"blue": "#0000ff",
|
||||
}
|
||||
cases := []struct {
|
||||
Case string
|
||||
Palettes *ansi.Palettes
|
||||
Palette ansi.Palette
|
||||
ExpectedPalette ansi.Palette
|
||||
}{
|
||||
{
|
||||
Case: "match",
|
||||
Palettes: &ansi.Palettes{
|
||||
Template: "{{ .Shell }}",
|
||||
List: map[string]ansi.Palette{
|
||||
"bash": palette,
|
||||
"zsh": {
|
||||
"red": "#ff0001",
|
||||
"blue": "#0000fb",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedPalette: palette,
|
||||
},
|
||||
{
|
||||
Case: "no match, no fallback",
|
||||
Palettes: &ansi.Palettes{
|
||||
Template: "{{ .Shell }}",
|
||||
List: map[string]ansi.Palette{
|
||||
"fish": palette,
|
||||
"zsh": {
|
||||
"red": "#ff0001",
|
||||
"blue": "#0000fb",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedPalette: nil,
|
||||
},
|
||||
{
|
||||
Case: "no match, default",
|
||||
Palettes: &ansi.Palettes{
|
||||
Template: "{{ .Shell }}",
|
||||
List: map[string]ansi.Palette{
|
||||
"zsh": {
|
||||
"red": "#ff0001",
|
||||
"blue": "#0000fb",
|
||||
},
|
||||
},
|
||||
},
|
||||
Palette: palette,
|
||||
ExpectedPalette: palette,
|
||||
},
|
||||
{
|
||||
Case: "no palettes",
|
||||
ExpectedPalette: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
env := &mock.MockedEnvironment{}
|
||||
env.On("TemplateCache").Return(&platform.TemplateCache{
|
||||
Env: map[string]string{},
|
||||
Shell: "bash",
|
||||
})
|
||||
env.On("DebugF", mock2.Anything, mock2.Anything).Return(nil)
|
||||
cfg := &Config{
|
||||
env: env,
|
||||
Palette: tc.Palette,
|
||||
Palettes: tc.Palettes,
|
||||
}
|
||||
got := cfg.getPalette()
|
||||
assert.Equal(t, tc.ExpectedPalette, got, tc.Case)
|
||||
}
|
||||
}
|
|
@ -1,288 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
)
|
||||
|
||||
var (
|
||||
cycle *ansi.Cycle = &ansi.Cycle{}
|
||||
)
|
||||
|
||||
type Engine struct {
|
||||
Config *Config
|
||||
Env platform.Environment
|
||||
Writer *ansi.Writer
|
||||
Plain bool
|
||||
|
||||
console strings.Builder
|
||||
currentLineLength int
|
||||
rprompt string
|
||||
rpromptLength int
|
||||
}
|
||||
|
||||
func (e *Engine) write(text string) {
|
||||
e.console.WriteString(text)
|
||||
}
|
||||
|
||||
func (e *Engine) string() string {
|
||||
text := e.console.String()
|
||||
e.console.Reset()
|
||||
return text
|
||||
}
|
||||
|
||||
func (e *Engine) canWriteRightBlock(rprompt bool) (int, bool) {
|
||||
if rprompt && (len(e.rprompt) == 0) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
consoleWidth, err := e.Env.TerminalWidth()
|
||||
if err != nil || consoleWidth == 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
promptWidth := e.currentLineLength
|
||||
availableSpace := consoleWidth - promptWidth
|
||||
|
||||
// spanning multiple lines
|
||||
if availableSpace < 0 {
|
||||
overflow := promptWidth % consoleWidth
|
||||
availableSpace = consoleWidth - overflow
|
||||
}
|
||||
|
||||
if rprompt {
|
||||
availableSpace -= e.rpromptLength
|
||||
}
|
||||
|
||||
promptBreathingRoom := 5
|
||||
if rprompt {
|
||||
promptBreathingRoom = 30
|
||||
}
|
||||
|
||||
canWrite := availableSpace >= promptBreathingRoom
|
||||
|
||||
return availableSpace, canWrite
|
||||
}
|
||||
|
||||
func (e *Engine) writeRPrompt() {
|
||||
space, OK := e.canWriteRightBlock(true)
|
||||
if !OK {
|
||||
return
|
||||
}
|
||||
e.write(e.Writer.SaveCursorPosition())
|
||||
e.write(strings.Repeat(" ", space))
|
||||
e.write(e.rprompt)
|
||||
e.write(e.Writer.RestoreCursorPosition())
|
||||
}
|
||||
|
||||
func (e *Engine) pwd() {
|
||||
// only print when supported
|
||||
sh := e.Env.Shell()
|
||||
if sh == shell.ELVISH || sh == shell.XONSH {
|
||||
return
|
||||
}
|
||||
// only print when relevant
|
||||
if len(e.Config.PWD) == 0 && !e.Config.OSC99 {
|
||||
return
|
||||
}
|
||||
|
||||
cwd := e.Env.Pwd()
|
||||
|
||||
// in BASH, we need to escape the path
|
||||
if e.Env.Shell() == shell.BASH {
|
||||
cwd = strings.ReplaceAll(cwd, `\`, `\\`)
|
||||
}
|
||||
|
||||
// Backwards compatibility for deprecated OSC99
|
||||
if e.Config.OSC99 {
|
||||
e.write(e.Writer.ConsolePwd(ansi.OSC99, "", "", cwd))
|
||||
return
|
||||
}
|
||||
// Allow template logic to define when to enable the PWD (when supported)
|
||||
tmpl := &template.Text{
|
||||
Template: e.Config.PWD,
|
||||
Env: e.Env,
|
||||
}
|
||||
|
||||
pwdType, err := tmpl.Render()
|
||||
if err != nil || len(pwdType) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
user := e.Env.User()
|
||||
host, _ := e.Env.Host()
|
||||
e.write(e.Writer.ConsolePwd(pwdType, user, host, cwd))
|
||||
}
|
||||
|
||||
func (e *Engine) newline() {
|
||||
// WARP terminal will remove \n from the prompt, so we hack a newline in
|
||||
if e.isWarp() {
|
||||
e.write(e.Writer.LineBreak())
|
||||
} else {
|
||||
e.write("\n")
|
||||
}
|
||||
e.currentLineLength = 0
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
padLength := remaining - blockLength
|
||||
if padLength <= 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// allow for easy color overrides and templates
|
||||
e.Writer.Write("", "", filler)
|
||||
filler, lenFiller := e.Writer.String()
|
||||
if lenFiller == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
repeat := padLength / lenFiller
|
||||
return strings.Repeat(filler, repeat), true
|
||||
}
|
||||
|
||||
func (e *Engine) getTitleTemplateText() string {
|
||||
tmpl := &template.Text{
|
||||
Template: e.Config.ConsoleTitleTemplate,
|
||||
Env: e.Env,
|
||||
}
|
||||
if text, err := tmpl.Render(); err == nil {
|
||||
return text
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *Engine) renderBlock(block *Block, cancelNewline bool) bool {
|
||||
defer e.patchPowerShellBleed()
|
||||
|
||||
// This is deprecated but we leave it in to not break configs
|
||||
// It is encouraged to used "newline": true on block level
|
||||
// rather than the standalone the linebreak block
|
||||
if block.Type == LineBreak {
|
||||
// do not print a newline to avoid a leading space
|
||||
// when we're printin the first primary prompt in
|
||||
// the shell
|
||||
if !cancelNewline {
|
||||
e.newline()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// when in bash, for rprompt blocks we need to write plain
|
||||
// and wrap in escaped mode or the prompt will not render correctly
|
||||
if e.Env.Shell() == shell.BASH && block.Type == RPrompt {
|
||||
block.InitPlain(e.Env, e.Config)
|
||||
} else {
|
||||
block.Init(e.Env, e.Writer)
|
||||
}
|
||||
|
||||
if !block.Enabled() {
|
||||
return false
|
||||
}
|
||||
|
||||
// do not print a newline to avoid a leading space
|
||||
// when we're printin the first primary prompt in
|
||||
// the shell
|
||||
if block.Newline && !cancelNewline {
|
||||
e.newline()
|
||||
}
|
||||
|
||||
text, length := block.RenderSegments()
|
||||
|
||||
// do not print anything when we don't have any text
|
||||
if length == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch block.Type { //nolint:exhaustive
|
||||
case Prompt:
|
||||
if block.VerticalOffset != 0 {
|
||||
e.write(e.Writer.ChangeLine(block.VerticalOffset))
|
||||
}
|
||||
|
||||
if block.Alignment == Left {
|
||||
e.currentLineLength += length
|
||||
e.write(text)
|
||||
return true
|
||||
}
|
||||
|
||||
if block.Alignment != Right {
|
||||
return false
|
||||
}
|
||||
|
||||
space, OK := e.canWriteRightBlock(false)
|
||||
// we can't print the right block as there's not enough room available
|
||||
if !OK {
|
||||
switch block.Overflow {
|
||||
case Break:
|
||||
e.newline()
|
||||
case Hide:
|
||||
// make sure to fill if needed
|
||||
if padText, OK := e.shouldFill(block.Filler, space, 0); OK {
|
||||
e.write(padText)
|
||||
}
|
||||
e.currentLineLength = 0
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
e.currentLineLength = 0
|
||||
}()
|
||||
|
||||
// validate if we have a filler and fill if needed
|
||||
if padText, OK := e.shouldFill(block.Filler, space, length); OK {
|
||||
e.write(padText)
|
||||
e.write(text)
|
||||
return true
|
||||
}
|
||||
|
||||
var prompt string
|
||||
space -= length
|
||||
|
||||
if space > 0 {
|
||||
prompt += strings.Repeat(" ", space)
|
||||
}
|
||||
|
||||
prompt += text
|
||||
e.write(prompt)
|
||||
case RPrompt:
|
||||
e.rprompt = text
|
||||
e.rpromptLength = length
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (e *Engine) patchPowerShellBleed() {
|
||||
// when in PowerShell, we need to clear the line after the prompt
|
||||
// to avoid the background being printed on the next line
|
||||
// when at the end of the buffer.
|
||||
// See https://github.com/JanDeDobbeleer/oh-my-posh/issues/65
|
||||
if e.Env.Shell() != shell.PWSH && e.Env.Shell() != shell.PWSH5 {
|
||||
return
|
||||
}
|
||||
|
||||
// only do this when enabled
|
||||
if !e.Config.PatchPwshBleed {
|
||||
return
|
||||
}
|
||||
|
||||
e.write(e.Writer.ClearAfter())
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var cases = []struct {
|
||||
Case string
|
||||
Config string
|
||||
}{
|
||||
{Case: ".omp.json suffix", Config: "~/jandedobbeleer.omp.json"},
|
||||
{Case: ".omp.yaml suffix", Config: "~/jandedobbeleer.omp.yaml"},
|
||||
{Case: ".omp.yml suffix", Config: "~/jandedobbeleer.omp.yml"},
|
||||
{Case: ".omp.toml suffix", Config: "~/jandedobbeleer.omp.toml"},
|
||||
{Case: ".json suffix", Config: "~/jandedobbeleer.json"},
|
||||
{Case: ".yaml suffix", Config: "~/jandedobbeleer.yaml"},
|
||||
{Case: ".yml suffix", Config: "~/jandedobbeleer.yml"},
|
||||
{Case: ".toml suffix", Config: "~/jandedobbeleer.toml"},
|
||||
}
|
||||
|
||||
func runImageTest(config, content string) (string, error) {
|
||||
poshImagePath := "jandedobbeleer.png"
|
||||
file, err := os.CreateTemp("", poshImagePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
writer := &ansi.Writer{}
|
||||
writer.Init(shell.GENERIC)
|
||||
image := &ImageRenderer{
|
||||
AnsiString: content,
|
||||
Ansi: writer,
|
||||
}
|
||||
|
||||
env := &platform.Shell{
|
||||
CmdFlags: &platform.Flags{
|
||||
Config: config,
|
||||
},
|
||||
}
|
||||
|
||||
err = image.Init(env)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = image.SavePNG()
|
||||
if err == nil {
|
||||
os.Remove(image.Path)
|
||||
}
|
||||
|
||||
return filepath.Base(image.Path), err
|
||||
}
|
||||
|
||||
func TestStringImageFileWithText(t *testing.T) {
|
||||
for _, tc := range cases {
|
||||
filename, err := runImageTest(tc.Config, "foobar")
|
||||
if connectionError, ok := err.(*ConnectionError); ok {
|
||||
t.Log(connectionError.Error())
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, "jandedobbeleer.png", filename, tc.Case)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringImageFileWithANSI(t *testing.T) {
|
||||
prompt := `[38;2;40;105;131m[0m[48;2;40;105;131m[38;2;224;222;244m jan [0m[38;2;40;105;131m[0m[38;2;224;222;244m [0m`
|
||||
for _, tc := range cases {
|
||||
filename, err := runImageTest(tc.Config, prompt)
|
||||
if connectionError, ok := err.(*ConnectionError); ok {
|
||||
t.Log(connectionError.Error())
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, "jandedobbeleer.png", filename, tc.Case)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
)
|
||||
|
||||
type codePoints map[uint64]uint64
|
||||
|
||||
func getGlyphCodePoints() (codePoints, error) {
|
||||
var codePoints = make(codePoints)
|
||||
|
||||
ctx, cncl := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
|
||||
defer cncl()
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://ohmyposh.dev/codepoints.csv", nil)
|
||||
if err != nil {
|
||||
return codePoints, &ConnectionError{reason: err.Error()}
|
||||
}
|
||||
|
||||
response, err := platform.Client.Do(request)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
bytes, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return codePoints, err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(bytes), "\n")
|
||||
|
||||
for _, line := range lines {
|
||||
fields := strings.Split(line, ",")
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
oldGlyph, err := strconv.ParseUint(fields[0], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newGlyph, err := strconv.ParseUint(fields[1], 16, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
codePoints[oldGlyph] = newGlyph
|
||||
}
|
||||
|
||||
return codePoints, nil
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetCodePoints(t *testing.T) {
|
||||
codepoints, err := getGlyphCodePoints()
|
||||
if connectionError, ok := err.(*ConnectionError); ok {
|
||||
t.Log(connectionError.Error())
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 1939, len(codepoints))
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
)
|
||||
|
||||
// New returns a prompt engine initialized with the
|
||||
// given configuration options, and is ready to print any
|
||||
// of the prompt components.
|
||||
func New(flags *platform.Flags) *Engine {
|
||||
env := &platform.Shell{
|
||||
CmdFlags: flags,
|
||||
}
|
||||
|
||||
env.Init()
|
||||
cfg := LoadConfig(env)
|
||||
|
||||
if cfg.PatchPwshBleed {
|
||||
patchPowerShellBleed(env.Shell(), flags)
|
||||
}
|
||||
|
||||
env.Var = cfg.Var
|
||||
flags.HasTransient = cfg.TransientPrompt != nil
|
||||
|
||||
ansiWriter := &ansi.Writer{
|
||||
TerminalBackground: shell.ConsoleBackgroundColor(env, cfg.TerminalBackground),
|
||||
AnsiColors: cfg.MakeColors(),
|
||||
Plain: flags.Plain,
|
||||
TrueColor: env.CmdFlags.TrueColor,
|
||||
}
|
||||
ansiWriter.Init(env.Shell())
|
||||
|
||||
eng := &Engine{
|
||||
Config: cfg,
|
||||
Env: env,
|
||||
Writer: ansiWriter,
|
||||
Plain: flags.Plain,
|
||||
}
|
||||
|
||||
return eng
|
||||
}
|
||||
|
||||
func patchPowerShellBleed(sh string, flags *platform.Flags) {
|
||||
// when in PowerShell, and force patching the bleed bug
|
||||
// we need to reduce the terminal width by 1 so the last
|
||||
// character isn't cut off by the ANSI escape sequences
|
||||
// See https://github.com/JanDeDobbeleer/oh-my-posh/issues/65
|
||||
if sh != shell.PWSH && sh != shell.PWSH5 {
|
||||
return
|
||||
}
|
||||
|
||||
// only do this when relevant
|
||||
if flags.TerminalWidth <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
flags.TerminalWidth--
|
||||
}
|
|
@ -1,239 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/template"
|
||||
)
|
||||
|
||||
type ExtraPromptType int
|
||||
|
||||
const (
|
||||
Transient ExtraPromptType = iota
|
||||
Valid
|
||||
Error
|
||||
Secondary
|
||||
Debug
|
||||
)
|
||||
|
||||
func (e *Engine) Primary() string {
|
||||
if e.Config.ShellIntegration {
|
||||
exitCode, _ := e.Env.StatusCodes()
|
||||
e.write(e.Writer.CommandFinished(exitCode, e.Env.Flags().NoExitCode))
|
||||
e.write(e.Writer.PromptStart())
|
||||
}
|
||||
|
||||
// cache a pointer to the color cycle
|
||||
cycle = &e.Config.Cycle
|
||||
var cancelNewline, didRender bool
|
||||
|
||||
for i, block := range e.Config.Blocks {
|
||||
// do not print a leading newline when we're at the first row and the prompt is cleared
|
||||
if i == 0 {
|
||||
row, _ := e.Env.CursorPosition()
|
||||
cancelNewline = e.Env.Flags().Cleared || e.Env.Flags().PromptCount == 1 || row == 1
|
||||
}
|
||||
|
||||
// skip setting a newline when we didn't print anything yet
|
||||
if i != 0 {
|
||||
cancelNewline = !didRender
|
||||
}
|
||||
|
||||
// only render rprompt for shells where we need it from the primary prompt
|
||||
renderRPrompt := true
|
||||
switch e.Env.Shell() {
|
||||
case shell.ELVISH, shell.FISH, shell.NU, shell.XONSH, shell.CMD:
|
||||
renderRPrompt = false
|
||||
}
|
||||
|
||||
if block.Type == RPrompt && !renderRPrompt {
|
||||
continue
|
||||
}
|
||||
|
||||
if e.renderBlock(block, cancelNewline) {
|
||||
didRender = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.Config.ConsoleTitleTemplate) > 0 && !e.Env.Flags().Plain {
|
||||
title := e.getTitleTemplateText()
|
||||
e.write(e.Writer.FormatTitle(title))
|
||||
}
|
||||
|
||||
if e.Config.FinalSpace {
|
||||
e.write(" ")
|
||||
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())
|
||||
}
|
||||
|
||||
e.pwd()
|
||||
|
||||
switch e.Env.Shell() {
|
||||
case shell.ZSH:
|
||||
if !e.Env.Flags().Eval {
|
||||
break
|
||||
}
|
||||
// Warp doesn't support RPROMPT so we need to write it manually
|
||||
if e.isWarp() {
|
||||
e.writeRPrompt()
|
||||
// escape double quotes contained in the prompt
|
||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.string(), `"`, `\"`))
|
||||
return prompt
|
||||
}
|
||||
// escape double quotes contained in the prompt
|
||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(e.string(), `"`, `\"`))
|
||||
prompt += fmt.Sprintf("\nRPROMPT=\"%s\"", e.rprompt)
|
||||
return prompt
|
||||
case shell.PWSH, shell.PWSH5, shell.GENERIC:
|
||||
e.writeRPrompt()
|
||||
case shell.BASH:
|
||||
space, OK := e.canWriteRightBlock(true)
|
||||
if !OK {
|
||||
break
|
||||
}
|
||||
// in bash, the entire rprompt needs to be escaped for the prompt to be interpreted correctly
|
||||
// see https://github.com/jandedobbeleer/oh-my-posh/pull/2398
|
||||
writer := &ansi.Writer{
|
||||
TrueColor: e.Env.Flags().TrueColor,
|
||||
}
|
||||
writer.Init(shell.GENERIC)
|
||||
prompt := writer.SaveCursorPosition()
|
||||
prompt += strings.Repeat(" ", space)
|
||||
prompt += e.rprompt
|
||||
prompt += writer.RestoreCursorPosition()
|
||||
prompt = e.Writer.FormatText(prompt)
|
||||
e.write(prompt)
|
||||
}
|
||||
|
||||
return e.string()
|
||||
}
|
||||
|
||||
func (e *Engine) ExtraPrompt(promptType ExtraPromptType) string {
|
||||
// populate env with latest context
|
||||
e.Env.LoadTemplateCache()
|
||||
var prompt *Segment
|
||||
switch promptType {
|
||||
case Debug:
|
||||
prompt = e.Config.DebugPrompt
|
||||
case Transient:
|
||||
prompt = e.Config.TransientPrompt
|
||||
case Valid:
|
||||
prompt = e.Config.ValidLine
|
||||
case Error:
|
||||
prompt = e.Config.ErrorLine
|
||||
case Secondary:
|
||||
prompt = e.Config.SecondaryPrompt
|
||||
}
|
||||
|
||||
if prompt == nil {
|
||||
prompt = &Segment{}
|
||||
}
|
||||
|
||||
getTemplate := func(template string) string {
|
||||
if len(template) != 0 {
|
||||
return template
|
||||
}
|
||||
switch promptType { //nolint: exhaustive
|
||||
case Debug:
|
||||
return "[DBG]: "
|
||||
case Transient:
|
||||
return "{{ .Shell }}> "
|
||||
case Secondary:
|
||||
return "> "
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
tmpl := &template.Text{
|
||||
Template: getTemplate(prompt.Template),
|
||||
Env: e.Env,
|
||||
}
|
||||
promptText, err := tmpl.Render()
|
||||
if err != nil {
|
||||
promptText = err.Error()
|
||||
}
|
||||
|
||||
if promptType == Transient && e.Config.ShellIntegration {
|
||||
exitCode, _ := e.Env.StatusCodes()
|
||||
e.write(e.Writer.CommandFinished(exitCode, e.Env.Flags().NoExitCode))
|
||||
e.write(e.Writer.PromptStart())
|
||||
}
|
||||
|
||||
foreground := prompt.ForegroundTemplates.FirstMatch(nil, e.Env, prompt.Foreground)
|
||||
background := prompt.BackgroundTemplates.FirstMatch(nil, e.Env, prompt.Background)
|
||||
e.Writer.SetColors(background, foreground)
|
||||
e.Writer.Write(background, foreground, promptText)
|
||||
|
||||
str, length := e.Writer.String()
|
||||
if promptType == Transient {
|
||||
consoleWidth, err := e.Env.TerminalWidth()
|
||||
if err == nil || consoleWidth != 0 {
|
||||
if padText, OK := e.shouldFill(prompt.Filler, consoleWidth, length); OK {
|
||||
str += padText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if promptType == Transient && e.Config.ShellIntegration {
|
||||
str += e.Writer.CommandStart()
|
||||
}
|
||||
|
||||
switch e.Env.Shell() {
|
||||
case shell.ZSH:
|
||||
// escape double quotes contained in the prompt
|
||||
if promptType == Transient {
|
||||
prompt := fmt.Sprintf("PS1=\"%s\"", strings.ReplaceAll(str, "\"", "\"\""))
|
||||
// empty RPROMPT
|
||||
prompt += "\nRPROMPT=\"\""
|
||||
return prompt
|
||||
}
|
||||
return str
|
||||
case shell.PWSH, shell.PWSH5:
|
||||
// Return the string and empty our buffer
|
||||
// clear the line afterwards to prevent text from being written on the same line
|
||||
// see https://github.com/JanDeDobbeleer/oh-my-posh/issues/3628
|
||||
return str + e.Writer.ClearAfter()
|
||||
case shell.CMD, shell.BASH, shell.FISH, shell.NU, shell.GENERIC:
|
||||
// Return the string and empty our buffer
|
||||
return str
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *Engine) RPrompt() string {
|
||||
filterRPromptBlock := func(blocks []*Block) *Block {
|
||||
for _, block := range blocks {
|
||||
if block.Type == RPrompt {
|
||||
return block
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
block := filterRPromptBlock(e.Config.Blocks)
|
||||
if block == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
block.Init(e.Env, e.Writer)
|
||||
if !block.Enabled() {
|
||||
return ""
|
||||
}
|
||||
|
||||
text, length := block.RenderSegments()
|
||||
e.rpromptLength = length
|
||||
return text
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/shell"
|
||||
)
|
||||
|
||||
func (e *Engine) Tooltip(tip string) string {
|
||||
tip = strings.Trim(tip, " ")
|
||||
var tooltip *Segment
|
||||
for _, tp := range e.Config.Tooltips {
|
||||
if !tp.shouldInvokeWithTip(tip) {
|
||||
continue
|
||||
}
|
||||
tooltip = tp
|
||||
}
|
||||
|
||||
if tooltip == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if err := tooltip.mapSegmentWithWriter(e.Env); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if !tooltip.writer.Enabled() {
|
||||
return ""
|
||||
}
|
||||
|
||||
tooltip.Enabled = true
|
||||
|
||||
// little hack to reuse the current logic
|
||||
block := &Block{
|
||||
Alignment: Right,
|
||||
Segments: []*Segment{tooltip},
|
||||
}
|
||||
|
||||
switch e.Env.Shell() {
|
||||
case shell.ZSH, shell.CMD, shell.FISH, shell.GENERIC:
|
||||
block.Init(e.Env, e.Writer)
|
||||
if !block.Enabled() {
|
||||
return ""
|
||||
}
|
||||
text, _ := block.RenderSegments()
|
||||
return text
|
||||
case shell.PWSH, shell.PWSH5:
|
||||
block.InitPlain(e.Env, e.Config)
|
||||
if !block.Enabled() {
|
||||
return ""
|
||||
}
|
||||
|
||||
consoleWidth, err := e.Env.TerminalWidth()
|
||||
if err != nil || consoleWidth == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
text, length := block.RenderSegments()
|
||||
|
||||
space := consoleWidth - e.Env.Flags().Column - length
|
||||
if space <= 0 {
|
||||
return ""
|
||||
}
|
||||
// clear from cursor to the end of the line in case a previous tooltip
|
||||
// is cut off and partially preserved, if the new one is shorter
|
||||
e.write(e.Writer.ClearAfter())
|
||||
e.write(strings.Repeat(" ", space))
|
||||
e.write(text)
|
||||
return e.string()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
|
@ -10,9 +10,14 @@ import (
|
|||
"github.com/charmbracelet/bubbles/spinner"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/terminal"
|
||||
)
|
||||
|
||||
var program *tea.Program
|
||||
var (
|
||||
program *tea.Program
|
||||
environment runtime.Environment
|
||||
)
|
||||
|
||||
const listHeight = 14
|
||||
|
||||
|
@ -113,6 +118,7 @@ func downloadFontZip(location string) {
|
|||
program.Send(errMsg(err))
|
||||
return
|
||||
}
|
||||
|
||||
program.Send(zipMsg(zipFile))
|
||||
}
|
||||
|
||||
|
@ -122,6 +128,7 @@ func installLocalFontZIP(zipFile string, user bool) {
|
|||
program.Send(errMsg(err))
|
||||
return
|
||||
}
|
||||
|
||||
installFontZIP(data, user)
|
||||
}
|
||||
|
||||
|
@ -131,6 +138,7 @@ func installFontZIP(zipFile []byte, user bool) {
|
|||
program.Send(errMsg(err))
|
||||
return
|
||||
}
|
||||
|
||||
program.Send(successMsg(families))
|
||||
}
|
||||
|
||||
|
@ -158,6 +166,7 @@ func (m *main) Init() tea.Cmd {
|
|||
}
|
||||
go getFontsList()
|
||||
}()
|
||||
|
||||
s := spinner.New()
|
||||
s.Spinner = spinner.Dot
|
||||
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("170"))
|
||||
|
@ -166,6 +175,7 @@ func (m *main) Init() tea.Cmd {
|
|||
if isLocalZipFile() {
|
||||
m.state = unzipFont
|
||||
}
|
||||
|
||||
return m.spinner.Tick
|
||||
}
|
||||
|
||||
|
@ -231,6 +241,10 @@ func (m *main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
return m, cmd
|
||||
}
|
||||
|
||||
if m.list == nil {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
lst, cmd := m.list.Update(msg)
|
||||
m.list = &lst
|
||||
return m, cmd
|
||||
|
@ -240,36 +254,48 @@ func (m *main) View() string {
|
|||
if m.err != nil {
|
||||
return textStyle.Render(m.err.Error())
|
||||
}
|
||||
|
||||
switch m.state {
|
||||
case getFonts:
|
||||
return textStyle.Render(fmt.Sprintf("%s Downloading font list", m.spinner.View()))
|
||||
return textStyle.Render(fmt.Sprintf("%s Downloading font list%s", m.spinner.View(), terminal.StartProgress()))
|
||||
case selectFont:
|
||||
return "\n" + m.list.View()
|
||||
return fmt.Sprintf("\n%s%s", m.list.View(), terminal.StopProgress())
|
||||
case downloadFont:
|
||||
return textStyle.Render(fmt.Sprintf("%s Downloading %s", m.spinner.View(), m.font))
|
||||
return textStyle.Render(fmt.Sprintf("%s Downloading %s%s", m.spinner.View(), m.font, terminal.StartProgress()))
|
||||
case unzipFont:
|
||||
return textStyle.Render(fmt.Sprintf("%s Extracting %s", m.spinner.View(), m.font))
|
||||
case installFont:
|
||||
return textStyle.Render(fmt.Sprintf("%s Installing %s", m.spinner.View(), m.font))
|
||||
case quit:
|
||||
return textStyle.Render("No need to install a new font? That's cool.")
|
||||
return textStyle.Render(fmt.Sprintf("No need to install a new font? That's cool.%s", terminal.StopProgress()))
|
||||
case done:
|
||||
var builder strings.Builder
|
||||
builder.WriteString(fmt.Sprintf("Successfully installed %s 🚀\n\n", m.font))
|
||||
|
||||
builder.WriteString(fmt.Sprintf("Successfully installed %s 🚀\n\n%s", m.font, terminal.StopProgress()))
|
||||
builder.WriteString("The following font families are now available for configuration:\n")
|
||||
for _, family := range m.families {
|
||||
builder.WriteString(fmt.Sprintf(" • %s\n", family))
|
||||
|
||||
for i, family := range m.families {
|
||||
builder.WriteString(fmt.Sprintf(" • %s", family))
|
||||
|
||||
if i < len(m.families)-1 {
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
return textStyle.Render(builder.String())
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func Run(font string, system bool) {
|
||||
func Run(font string, env runtime.Environment) {
|
||||
main := &main{
|
||||
font: font,
|
||||
system: system,
|
||||
system: env.Root(),
|
||||
}
|
||||
|
||||
environment = env
|
||||
|
||||
program = tea.NewProgram(main)
|
||||
if _, err := program.Run(); err != nil {
|
||||
print("Error running program: %v", err)
|
||||
|
|
|
@ -8,10 +8,10 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
httplib "net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/http"
|
||||
)
|
||||
|
||||
func Download(fontPath string) ([]byte, error) {
|
||||
|
@ -33,22 +33,22 @@ func Download(fontPath string) ([]byte, error) {
|
|||
}
|
||||
|
||||
func isZipFile(data []byte) bool {
|
||||
contentType := http.DetectContentType(data)
|
||||
contentType := httplib.DetectContentType(data)
|
||||
return contentType == "application/zip"
|
||||
}
|
||||
|
||||
func getRemoteFile(location string) (data []byte, err error) {
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", location, nil)
|
||||
req, err := httplib.NewRequestWithContext(context.Background(), "GET", location, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := platform.Client.Do(req)
|
||||
resp, err := http.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode != httplib.StatusOK {
|
||||
return data, fmt.Errorf("Failed to download zip file: %s\n→ %s", resp.Status, location)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,13 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
httplib "net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/platform"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/cache"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/http"
|
||||
)
|
||||
|
||||
type release struct {
|
||||
|
@ -26,6 +27,10 @@ type Asset struct {
|
|||
func (a Asset) FilterValue() string { return a.Name }
|
||||
|
||||
func Fonts() ([]*Asset, error) {
|
||||
if assets, err := getCachedFontData(); err == nil {
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
assets, err := fetchFontAssets("ryanoasis/nerd-fonts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -39,9 +44,43 @@ func Fonts() ([]*Asset, error) {
|
|||
assets = append(assets, cascadiaCode...)
|
||||
sort.Slice(assets, func(i, j int) bool { return assets[i].Name < assets[j].Name })
|
||||
|
||||
setCachedFontData(assets)
|
||||
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
func getCachedFontData() ([]*Asset, error) {
|
||||
if environment == nil {
|
||||
return nil, errors.New("environment not set")
|
||||
}
|
||||
|
||||
list, OK := environment.Cache().Get(cache.FONTLISTCACHE)
|
||||
if !OK {
|
||||
return nil, errors.New("cache not found")
|
||||
}
|
||||
|
||||
assets := make([]*Asset, 0)
|
||||
err := json.Unmarshal([]byte(list), &assets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
func setCachedFontData(assets []*Asset) {
|
||||
if environment == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(assets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
environment.Cache().Set(cache.FONTLISTCACHE, string(data), cache.ONEDAY)
|
||||
}
|
||||
|
||||
func CascadiaCode() ([]*Asset, error) {
|
||||
return fetchFontAssets("microsoft/cascadia-code")
|
||||
}
|
||||
|
@ -51,14 +90,14 @@ func fetchFontAssets(repo string) ([]*Asset, error) {
|
|||
defer cancelF()
|
||||
|
||||
repoURL := "https://api.github.com/repos/" + repo + "/releases/latest"
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", repoURL, nil)
|
||||
req, err := httplib.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 {
|
||||
response, err := http.HTTPClient.Do(req)
|
||||
if err != nil || response.StatusCode != httplib.StatusOK {
|
||||
return nil, fmt.Errorf("failed to get %s release", repo)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,11 @@ import (
|
|||
"bytes"
|
||||
"io"
|
||||
"path"
|
||||
stdruntime "runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime"
|
||||
"github.com/jandedobbeleer/oh-my-posh/src/runtime/cmd"
|
||||
)
|
||||
|
||||
func contains[S ~[]E, E comparable](s S, e E) bool {
|
||||
|
@ -31,6 +35,12 @@ func InstallZIP(data []byte, user bool) ([]string, error) {
|
|||
fonts := make(map[string]*Font)
|
||||
|
||||
for _, zf := range zipReader.File {
|
||||
// prevent zipslip attacks
|
||||
// https://security.snyk.io/research/zip-slip-vulnerability
|
||||
if strings.Contains(zf.Name, "..") {
|
||||
continue
|
||||
}
|
||||
|
||||
rc, err := zf.Open()
|
||||
if err != nil {
|
||||
return families, err
|
||||
|
@ -42,7 +52,7 @@ func InstallZIP(data []byte, user bool) ([]string, error) {
|
|||
return families, err
|
||||
}
|
||||
|
||||
fontData, err := newFont(zf.Name, data)
|
||||
fontData, err := newFont(path.Base(zf.Name), data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
@ -69,5 +79,10 @@ func InstallZIP(data []byte, user bool) ([]string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Update the font cache when installing fonts on Linux
|
||||
if stdruntime.GOOS == runtime.LINUX || stdruntime.GOOS == runtime.DARWIN {
|
||||
_, _ = cmd.Run("fc-cache", "-f")
|
||||
}
|
||||
|
||||
return families, nil
|
||||
}
|
||||
|
|
|
@ -10,15 +10,23 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// FontsDir denotes the path to the user's fonts directory on Unix-like systems.
|
||||
var FontsDir = path.Join(os.Getenv("HOME"), "/.local/share/fonts")
|
||||
var (
|
||||
fontsDir = path.Join(os.Getenv("HOME"), "/.local/share/fonts")
|
||||
systemFontsDir = "/usr/share/fonts"
|
||||
)
|
||||
|
||||
func install(font *Font, _ bool) error {
|
||||
// If we're running as root, install the font system-wide.
|
||||
targetDir := fontsDir
|
||||
if os.Geteuid() == 0 {
|
||||
targetDir = systemFontsDir
|
||||
}
|
||||
|
||||
// On Linux, fontconfig can understand subdirectories. So, to keep the
|
||||
// font directory clean, install all font files for a particular font
|
||||
// family into a subdirectory named after the family (with hyphens instead
|
||||
// of spaces).
|
||||
fullPath := path.Join(FontsDir,
|
||||
fullPath := path.Join(targetDir,
|
||||
strings.ToLower(strings.ReplaceAll(font.Family, " ", "-")),
|
||||
path.Base(font.FileName))
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ func install(font *Font, admin bool) error {
|
|||
return fmt.Errorf("Unable to remove existing font file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
err := os.WriteFile(fullPath, font.Data, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to write font file: %s", err.Error())
|
||||
|
|
51
src/go.mod
51
src/go.mod
|
@ -12,35 +12,35 @@ require (
|
|||
github.com/fogleman/gg v1.3.0
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.1 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.24.4
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
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.21.0 // indirect
|
||||
golang.org/x/image v0.16.0
|
||||
golang.org/x/sys v0.20.0
|
||||
golang.org/x/text v0.15.0
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/image v0.19.0
|
||||
golang.org/x/sys v0.23.0
|
||||
golang.org/x/text v0.17.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.26.2
|
||||
github.com/charmbracelet/lipgloss v0.10.0
|
||||
github.com/charmbracelet/bubbletea v0.26.6
|
||||
github.com/charmbracelet/lipgloss v0.12.1
|
||||
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/gookit/goutil v0.6.16
|
||||
github.com/hashicorp/hcl/v2 v2.21.0
|
||||
github.com/mattn/go-runewidth v0.0.16
|
||||
github.com/pelletier/go-toml/v2 v2.2.2
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
|
||||
golang.org/x/mod v0.17.0
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
||||
golang.org/x/mod v0.20.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
@ -50,13 +50,13 @@ require (
|
|||
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-20240226150601-1dcf7310316a // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // 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
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.13 // indirect
|
||||
github.com/tklauser/numcpus v0.7.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
)
|
||||
|
||||
|
@ -66,9 +66,13 @@ require (
|
|||
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/charmbracelet/x/ansi v0.1.4 // indirect
|
||||
github.com/charmbracelet/x/input v0.1.2 // indirect
|
||||
github.com/charmbracelet/x/term v0.1.1 // indirect
|
||||
github.com/charmbracelet/x/windows v0.1.2 // 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/fatih/color v1.17.0 // 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
|
||||
|
@ -84,12 +88,11 @@ require (
|
|||
github.com/rivo/uniseg v0.4.7 // 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/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // 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
|
||||
github.com/zclconf/go-cty v1.14.4 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
)
|
||||
|
||||
|
@ -97,4 +100,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.11.3 => github.com/jandedobbeleer/go-yaml v1.11.3-2
|
||||
replace github.com/goccy/go-yaml v1.11.3 => github.com/jandedobbeleer/go-yaml v1.11.3-4
|
||||
|
|
118
src/go.sum
118
src/go.sum
|
@ -25,11 +25,19 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
|
|||
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.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/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s=
|
||||
github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk=
|
||||
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
|
||||
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
|
||||
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
|
||||
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0=
|
||||
github.com/charmbracelet/x/input v0.1.2/go.mod h1:LGBim0maUY4Pitjn/4fHnuXb4KirU3DODsyuHuXdOyA=
|
||||
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
|
||||
github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw=
|
||||
github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg=
|
||||
github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/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=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -40,8 +48,8 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6
|
|||
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=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
|
@ -61,8 +69,6 @@ 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=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
@ -70,13 +76,13 @@ 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/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
|
||||
github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY=
|
||||
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/gookit/goutil v0.6.16 h1:9fRMCF4X9abdRD5+2HhBS/GwafjBlTUBjRtA5dgkvuw=
|
||||
github.com/gookit/goutil v0.6.16/go.mod h1:op2q8AoPDFSiY2+qkHxcBWQMYxOLQ1GbLXqe7vrwscI=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
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=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
|
@ -84,8 +90,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/jandedobbeleer/go-yaml v1.11.3-4 h1:+S8q91z8LmfSxwhon2ndSk5pZdFpD3aYMyo2tUQb6P0=
|
||||
github.com/jandedobbeleer/go-yaml v1.11.3-4/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=
|
||||
|
@ -101,9 +107,8 @@ github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
|||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
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-20240226150601-1dcf7310316a h1:3Bm7EwfUQUvhNeKIkUct/gl9eod1TcXuj8stxvi/GoI=
|
||||
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
|
||||
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/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=
|
||||
|
@ -112,8 +117,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
|
@ -134,7 +139,6 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
|
|||
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-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=
|
||||
|
@ -148,22 +152,22 @@ 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.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
|
||||
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
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=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d h1:lvCTyBbr36+tqMccdGMwuEU+hjux/zL6xSmf5S9ITaA=
|
||||
github.com/shurcooL/gofontwoff v0.0.0-20181114050219-180f79e6909d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -179,12 +183,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||
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=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
|
||||
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
|
||||
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/wayneashleyberry/terminal-dimensions v1.1.0 h1:EB7cIzBdsOzAgmhTUtTTQXBByuPheP/Zv1zL2BRPY6g=
|
||||
github.com/wayneashleyberry/terminal-dimensions v1.1.0/go.mod h1:2lc/0eWCObmhRczn2SdGSQtgBooLUzIotkkEGXqghyg=
|
||||
|
@ -193,30 +195,30 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJu
|
|||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
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.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=
|
||||
github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
|
||||
github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
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.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/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
|
||||
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
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/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
||||
golang.org/x/mod v0.20.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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.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=
|
||||
|
@ -230,30 +232,24 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.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/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.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.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.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
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.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue