diff --git a/.all-contributorsrc b/.all-contributorsrc index 8ab1f7cb..e2ed65d5 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2898,6 +2898,15 @@ "code", "design" ] + }, + { + "login": "pashagolub", + "name": "Pavlo Golub", + "avatar_url": "https://avatars.githubusercontent.com/u/9463113?v=4", + "profile": "https://pashagolub.github.io/blog", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index cf1dde81..4f175a06 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,10 +24,10 @@ jobs: - name: Install Go 🗳 uses: ./.github/workflows/composite/bootstrap-go - name: Initialize CodeQL - uses: github/codeql-action/init@aa578102511db1f4524ed59b8cc2bae4f6e88195 + uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@aa578102511db1f4524ed59b8cc2bae4f6e88195 + uses: github/codeql-action/autobuild@df409f7d9260372bd5f19e5b04e83cb3c43714ae - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@aa578102511db1f4524ed59b8cc2bae4f6e88195 + uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa5a3a16..932a287f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -124,7 +124,7 @@ jobs: az storage blob upload-batch --destination v${{ needs.changelog.outputs.version }} --source . az storage blob upload-batch --destination latest --overwrite true --source . - name: Release 🎓 - uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 + uses: softprops/action-gh-release@7b4da11513bf3f43f9999e90eabced41ab8bb048 with: tag_name: ${{ needs.changelog.outputs.tag }} body: ${{ needs.changelog.outputs.body }} diff --git a/src/cli/font.go b/src/cli/font.go index cf1070ed..a1212445 100644 --- a/src/cli/font.go +++ b/src/cli/font.go @@ -11,7 +11,8 @@ import ( ) var ( - // fontCmd can work with fonts + ttf bool + fontCmd = &cobra.Command{ Use: "font [install|configure]", Short: "Manage fonts", @@ -46,7 +47,7 @@ This command is used to install fonts and configure the font in your terminal. terminal.Init(env.Shell()) - font.Run(fontName, env) + font.Run(fontName, env.Cache(), env.Root(), ttf) return case "configure": @@ -59,5 +60,6 @@ This command is used to install fonts and configure the font in your terminal. ) func init() { + fontCmd.Flags().BoolVar(&ttf, "ttf", false, "fetch the TTF version of the font") RootCmd.AddCommand(fontCmd) } diff --git a/src/config/block.go b/src/config/block.go index bb9fda95..ff9dfa09 100644 --- a/src/config/block.go +++ b/src/config/block.go @@ -36,4 +36,5 @@ type Block struct { MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"` MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"` Newline bool `json:"newline,omitempty" toml:"newline,omitempty"` + Force bool `json:"force,omitempty" toml:"force,omitempty"` } diff --git a/src/config/segment.go b/src/config/segment.go index f1b14751..c86f6262 100644 --- a/src/config/segment.go +++ b/src/config/segment.go @@ -72,6 +72,7 @@ type Segment struct { Enabled bool `json:"-" toml:"-"` Newline bool `json:"newline,omitempty" toml:"newline,omitempty"` InvertPowerline bool `json:"invert_powerline,omitempty" toml:"invert_powerline,omitempty"` + restored bool `json:"-" toml:"-"` } func (segment *Segment) Name() string { @@ -238,11 +239,13 @@ func (segment *Segment) restoreCache() bool { log.Debug("restored segment from cache: ", segment.Name()) + segment.restored = true + return true } func (segment *Segment) setCache() { - if !segment.hasCache() { + if segment.restored || !segment.hasCache() { return } diff --git a/src/font/cli.go b/src/font/cli.go index 424caf0c..1a495b1d 100644 --- a/src/font/cli.go +++ b/src/font/cli.go @@ -10,13 +10,13 @@ import ( "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/jandedobbeleer/oh-my-posh/src/runtime" + cache_ "github.com/jandedobbeleer/oh-my-posh/src/cache" "github.com/jandedobbeleer/oh-my-posh/src/terminal" ) var ( - program *tea.Program - environment runtime.Environment + program *tea.Program + cache cache_.Cache ) const listHeight = 14 @@ -79,6 +79,7 @@ type main struct { spinner spinner.Model state state system bool + ttf bool } func (m *main) buildFontList(nerdFonts []*Asset) { @@ -120,18 +121,18 @@ func downloadFontZip(location string) { program.Send(zipMsg(zipFile)) } -func installLocalFontZIP(zipFile string, user bool) { +func installLocalFontZIP(zipFile string, user, ttf bool) { data, err := os.ReadFile(zipFile) if err != nil { program.Send(errMsg(err)) return } - installFontZIP(data, user) + installFontZIP(data, user, ttf) } -func installFontZIP(zipFile []byte, user bool) { - families, err := InstallZIP(zipFile, user) +func installFontZIP(zipFile []byte, user, ttf bool) { + families, err := InstallZIP(zipFile, user, ttf) if err != nil { program.Send(errMsg(err)) return @@ -159,7 +160,7 @@ func (m *main) Init() tea.Cmd { defer func() { if isLocalZipFile() { - go installLocalFontZIP(m.font, m.system) + go installLocalFontZIP(m.font, m.system, m.ttf) return } go getFontsList() @@ -239,7 +240,7 @@ func (m *main) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case zipMsg: m.state = installFont defer func() { - go installFontZIP(msg, m.system) + go installFontZIP(msg, m.system, m.ttf) }() m.spinner.Spinner = spinner.Dot return m, m.spinner.Tick @@ -290,7 +291,7 @@ func (m *main) View() string { var builder strings.Builder 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") + builder.WriteString("The following font families are now available for configuration:\n\n") for i, family := range m.families { builder.WriteString(fmt.Sprintf(" • %s", family)) @@ -306,13 +307,14 @@ func (m *main) View() string { return "" } -func Run(font string, env runtime.Environment) { +func Run(font string, ch cache_.Cache, root, ttf bool) { main := &main{ font: font, - system: env.Root(), + system: root, + ttf: ttf, } - environment = env + cache = ch program = tea.NewProgram(main) if _, err := program.Run(); err != nil { diff --git a/src/font/fonts.go b/src/font/fonts.go index 12c53bdf..db022f3c 100644 --- a/src/font/fonts.go +++ b/src/font/fonts.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/jandedobbeleer/oh-my-posh/src/cache" + cache_ "github.com/jandedobbeleer/oh-my-posh/src/cache" "github.com/jandedobbeleer/oh-my-posh/src/runtime/http" ) @@ -50,11 +50,11 @@ func Fonts() ([]*Asset, error) { } func getCachedFontData() ([]*Asset, error) { - if environment == nil { + if cache == nil { return nil, errors.New("environment not set") } - list, OK := environment.Cache().Get(cache.FONTLISTCACHE) + list, OK := cache.Get(cache_.FONTLISTCACHE) if !OK { return nil, errors.New("cache not found") } @@ -69,7 +69,7 @@ func getCachedFontData() ([]*Asset, error) { } func setCachedFontData(assets []*Asset) { - if environment == nil { + if cache == nil { return } @@ -78,7 +78,7 @@ func setCachedFontData(assets []*Asset) { return } - environment.Cache().Set(cache.FONTLISTCACHE, string(data), cache.ONEDAY) + cache.Set(cache_.FONTLISTCACHE, string(data), cache_.ONEDAY) } func CascadiaCode() ([]*Asset, error) { diff --git a/src/font/install.go b/src/font/install.go index 326d2b2e..c4feff8b 100644 --- a/src/font/install.go +++ b/src/font/install.go @@ -8,6 +8,7 @@ import ( "io" "path" stdruntime "runtime" + "slices" "strings" "github.com/jandedobbeleer/oh-my-posh/src/runtime" @@ -20,10 +21,17 @@ func contains[S ~[]E, E comparable](s S, e E) bool { return true } } + return false } -func InstallZIP(data []byte, user bool) ([]string, error) { +func InstallZIP(data []byte, user, ttf bool) ([]string, error) { + // prefer OTF over TTF; otherwise prefer the first font we find + extension := ".otf" + if ttf { + extension = ".ttf" + } + var families []string bytesReader := bytes.NewReader(data) @@ -45,6 +53,7 @@ func InstallZIP(data []byte, user bool) ([]string, error) { if err != nil { return families, err } + defer rc.Close() data, err := io.ReadAll(rc) @@ -59,13 +68,14 @@ func InstallZIP(data []byte, user bool) ([]string, error) { if _, found := fonts[fontData.Name]; !found { fonts[fontData.Name] = fontData - } else { - // prefer OTF over TTF; otherwise prefer the first font we find - first := strings.ToLower(path.Ext(fonts[fontData.Name].FileName)) - second := strings.ToLower(path.Ext(fontData.FileName)) - if first != second && second == ".otf" { - fonts[fontData.Name] = fontData - } + continue + } + + // respect the user's preference for TTF or OTF + first := strings.ToLower(path.Ext(fonts[fontData.Name].FileName)) + second := strings.ToLower(path.Ext(fontData.FileName)) + if first != second && second == extension { + fonts[fontData.Name] = fontData } } @@ -74,7 +84,7 @@ func InstallZIP(data []byte, user bool) ([]string, error) { return families, err } - if !contains(families, font.Family) { + if found := contains(families, font.Family); !found { families = append(families, font.Family) } } @@ -84,5 +94,7 @@ func InstallZIP(data []byte, user bool) ([]string, error) { _, _ = cmd.Run("fc-cache", "-f") } + slices.Sort(families) + return families, nil } diff --git a/src/go.mod b/src/go.mod index b8364f5c..b1c6327c 100644 --- a/src/go.mod +++ b/src/go.mod @@ -19,7 +19,7 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 github.com/wayneashleyberry/terminal-dimensions v1.1.0 - golang.org/x/crypto v0.26.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/image v0.23.0 golang.org/x/sys v0.28.0 golang.org/x/text v0.21.0 @@ -31,9 +31,9 @@ require ( github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.2.4 github.com/charmbracelet/lipgloss v1.0.0 - github.com/goccy/go-json v0.10.3 + github.com/goccy/go-json v0.10.4 github.com/goccy/go-yaml v1.11.3 - github.com/gookit/goutil v0.6.17 + github.com/gookit/goutil v0.6.18 github.com/hashicorp/hcl/v2 v2.23.0 github.com/mattn/go-runewidth v0.0.16 github.com/pelletier/go-toml/v2 v2.2.3 diff --git a/src/go.sum b/src/go.sum index f9698fec..aa2eb7a3 100644 --- a/src/go.sum +++ b/src/go.sum @@ -62,8 +62,8 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= -github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -72,8 +72,8 @@ 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.17 h1:SxmbDz2sn2V+O+xJjJhJT/sq1/kQh6rCJ7vLBiRPZjI= -github.com/gookit/goutil v0.6.17/go.mod h1:rSw1LchE1I3TDWITZvefoAC9tS09SFu3lHXLCV7EaEY= +github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw= +github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA= github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos= github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= @@ -175,8 +175,8 @@ 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.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 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.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= diff --git a/src/prompt/engine.go b/src/prompt/engine.go index 2ed895ab..160a5d02 100644 --- a/src/prompt/engine.go +++ b/src/prompt/engine.go @@ -196,8 +196,8 @@ func (e *Engine) getTitleTemplateText() string { func (e *Engine) renderBlock(block *config.Block, cancelNewline bool) bool { text, length := e.writeBlockSegments(block) - // do not print anything when we don't have any text - if length == 0 { + // do not print anything when we don't have any text unless forced + if !block.Force && length == 0 { return false } diff --git a/src/segments/python.go b/src/segments/python.go index 68015c05..9b45a061 100644 --- a/src/segments/python.go +++ b/src/segments/python.go @@ -7,6 +7,7 @@ import ( "slices" "strings" + "github.com/jandedobbeleer/oh-my-posh/src/log" "github.com/jandedobbeleer/oh-my-posh/src/properties" "github.com/jandedobbeleer/oh-my-posh/src/runtime/path" ) @@ -68,6 +69,7 @@ func (p *Python) loadContext() { p.Venv = prompt return } + venvVars := []string{ "VIRTUAL_ENV", "CONDA_ENV_PATH", @@ -88,9 +90,11 @@ func (p *Python) loadContext() { } name := path.Base(venv) + log.Debugf("virtual env name: %s", name) if folderNameFallback && slices.Contains(defaultVenvNames, name) { venv = strings.TrimSuffix(venv, name) name = path.Base(venv) + log.Debugf("virtual env name (fallback): %s", name) } if p.canUseVenvName(name) { @@ -108,12 +112,15 @@ func (p *Python) canUseVenvName(name string) bool { if p.language.props.GetBool(properties.DisplayDefault, true) { return true } + invalidNames := [2]string{"system", "base"} for _, a := range invalidNames { if a == name { + log.Debugf("virtual env name %s is invalid", name) return false } } + return true } @@ -193,6 +200,7 @@ func (p *Python) pyvenvCfgPrompt() string { if len(lineSplit) != 2 { continue } + key := strings.TrimSpace(lineSplit[0]) if key == "prompt" { value := strings.TrimSpace(lineSplit[1]) diff --git a/website/docs/configuration/block.mdx b/website/docs/configuration/block.mdx index 59338a3d..9c488a2c 100644 --- a/website/docs/configuration/block.mdx +++ b/website/docs/configuration/block.mdx @@ -32,6 +32,7 @@ import Config from "@site/src/components/Config.js"; | `leading_diamond` | `string` | | `trailing_diamond` | `string` | | `segments` | `array` | +| `force` | `boolean` | ### Type @@ -114,5 +115,9 @@ with the same trailing diamond, regardless of which segment is enabled or not. Array of one or more [segments][segment]. +### Force + +When set to `true`, the block will always be rendered, even if all segments are empty. Defaults to `false`. + [color-overrides]: /docs/configuration/colors#color-overrides [segment]: segment.mdx diff --git a/website/docs/contributors.md b/website/docs/contributors.md index 6b5a019c..eb45ee78 100644 --- a/website/docs/contributors.md +++ b/website/docs/contributors.md @@ -407,6 +407,9 @@ Thanks goes to these wonderful people ([emoji key][acek]):