From 7983e2a98dd33d247f8d2fec98e7ffd50f2dbb8f Mon Sep 17 00:00:00 2001 From: "Dieckman, Eric" Date: Wed, 18 Dec 2024 14:11:11 -0600 Subject: [PATCH 1/3] feat(segments): Add Vite segment --- src/config/segment_types.go | 3 ++ src/segments/vite.go | 26 ++++++++++++ themes/schema.json | 49 +++++++++++++++++++++++ website/docs/segments/cli/vite.mdx | 63 ++++++++++++++++++++++++++++++ website/sidebars.js | 1 + 5 files changed, 142 insertions(+) create mode 100644 src/segments/vite.go create mode 100644 website/docs/segments/cli/vite.mdx diff --git a/src/config/segment_types.go b/src/config/segment_types.go index 63df09bd..e4b6213e 100644 --- a/src/config/segment_types.go +++ b/src/config/segment_types.go @@ -213,6 +213,8 @@ const ( UPGRADE SegmentType = "upgrade" // VALA writes the active vala version VALA SegmentType = "vala" + // VUE writes the current vite version + VITE SegmentType = "vite" // WAKATIME writes tracked time spend in dev editors WAKATIME SegmentType = "wakatime" // WINREG queries the Windows registry. @@ -324,6 +326,7 @@ var Segments = map[SegmentType]func() SegmentWriter{ UNITY: func() SegmentWriter { return &segments.Unity{} }, UPGRADE: func() SegmentWriter { return &segments.Upgrade{} }, VALA: func() SegmentWriter { return &segments.Vala{} }, + VITE: func() SegmentWriter { return &segments.Vite{} }, WAKATIME: func() SegmentWriter { return &segments.Wakatime{} }, WINREG: func() SegmentWriter { return &segments.WindowsRegistry{} }, WITHINGS: func() SegmentWriter { return &segments.Withings{} }, diff --git a/src/segments/vite.go b/src/segments/vite.go new file mode 100644 index 00000000..650ef750 --- /dev/null +++ b/src/segments/vite.go @@ -0,0 +1,26 @@ +package segments + +type Vite struct { + language +} + +func (v *Vite) Template() string { + return languageTemplate +} + +func (v *Vite) Enabled() bool { + v.extensions = []string{"vite.config.*"} + v.commands = []*cmd{ + { + regex: `(?:(?P((?P[0-9]+).(?P[0-9]+).(?P[0-9]+))))`, + getVersion: v.getVersion, + }, + } + v.versionURLTemplate = "https://github.com/vitejs/vite/releases/tag/{{.Full}}" + + return v.language.Enabled() +} + +func (v *Vite) getVersion() (string, error) { + return v.nodePackageVersion("vite") +} diff --git a/themes/schema.json b/themes/schema.json index 1318b394..f568b96c 100644 --- a/themes/schema.json +++ b/themes/schema.json @@ -399,6 +399,7 @@ "unity", "upgrade", "vala", + "vite", "wakatime", "winreg", "withings", @@ -5019,6 +5020,54 @@ } } } + }, + { + "if": { + "properties": { + "type": { + "const": "vite" + } + } + }, + "then": { + "title": "Vite Segment", + "description": "https://ohmyposh.dev/docs/segments/cli/vite", + "properties": { + "properties": { + "properties": { + "home_enabled": { + "$ref": "#/definitions/home_enabled" + }, + "fetch_version": { + "$ref": "#/definitions/fetch_version" + }, + "missing_command_text": { + "$ref": "#/definitions/missing_command_text" + }, + "display_mode": { + "$ref": "#/definitions/display_mode" + }, + "version_url_template": { + "$ref": "#/definitions/version_url_template" + }, + "extensions": { + "type": "array", + "title": "Extensions", + "description": "The extensions to look for when determining if the current directory is an Vite project", + "default": [ + "vite.config.*" + ], + "items": { + "type": "string" + } + }, + "folders": { + "$ref": "#/definitions/folders" + } + } + } + } + } } ] } diff --git a/website/docs/segments/cli/vite.mdx b/website/docs/segments/cli/vite.mdx new file mode 100644 index 00000000..4a3ff1c8 --- /dev/null +++ b/website/docs/segments/cli/vite.mdx @@ -0,0 +1,63 @@ +--- +id: vite +title: Vite +sidebar_label: Vite +--- + +## What + +Display the currently active [Vite][vite] version. + +## Sample Configuration + +import Config from "@site/src/components/Config.js"; + + + +## Properties + +| Name | Type | Default | Description | +| ---------------------- | :--------: | :------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `home_enabled` | `boolean` | `false` | display the segment in the HOME folder or not | +| `fetch_version` | `boolean` | `true` | fetch the vite version | +| `cache_duration` | `string` | `24h` | the duration for which the version will be cached. The duration is a string in the format `1h2m3s` and is parsed using the [time.ParseDuration] function from the Go standard library. To disable the cache, use `none` | +| `missing_command_text` | `string` | | text to display when the command is missing | +| `display_mode` | `string` | `context` |
  • `always`: the segment is always displayed
  • `files`: the segment is only displayed when file `extensions` listed are present
  • `context`: displays the segment when the environment or files is active
| +| `version_url_template` | `string` | | a go [text/template][go-text-template] [template][templates] that creates the URL of the version info / release notes | +| `extensions` | `[]string` | `vite.config.*` | allows to override the default list of file extensions to validate | +| `folders` | `[]string` | | allows to override the list of folder names to validate | + +## Template ([info][templates]) + +:::note default template + +```template +{{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} +``` + +::: + +### Properties + +| Name | Type | Description | +| -------- | -------- | -------------------------------------------------- | +| `.Full` | `string` | the full version | +| `.Major` | `string` | major number | +| `.Minor` | `string` | minor number | +| `.Patch` | `string` | patch number | +| `.URL` | `string` | URL of the version info / release notes | +| `.Error` | `string` | error encountered when fetching the version string | + +[go-text-template]: https://golang.org/pkg/text/template/ +[templates]: /docs/configuration/templates +[vite]: https://vite.dev/ +[time.ParseDuration]: https://golang.org/pkg/time/#ParseDuration diff --git a/website/sidebars.js b/website/sidebars.js index d3326e30..b84a5119 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -89,6 +89,7 @@ module.exports = { "segments/cli/ui5tooling", "segments/cli/umbraco", "segments/cli/unity", + "segments/cli/vite", "segments/cli/xmake", "segments/cli/yarn", ] From b4d0b423954e724119e765f185e1de68ee39fa9a Mon Sep 17 00:00:00 2001 From: Jan De Dobbeleer Date: Thu, 19 Dec 2024 20:33:46 +0100 Subject: [PATCH 2/3] feat(font): cache zip files --- src/font/cli.go | 93 +++++++++++++++++++---------- src/font/download.go | 40 +++++++++++-- src/font/fonts.go | 42 ++++++++++--- src/font/install.go | 2 +- src/image/image.go | 4 +- website/docs/installation/fonts.mdx | 6 -- 6 files changed, 132 insertions(+), 55 deletions(-) diff --git a/src/font/cli.go b/src/font/cli.go index 64a995b9..e9a32eef 100644 --- a/src/font/cli.go +++ b/src/font/cli.go @@ -72,14 +72,13 @@ const ( ) type main struct { - err error - list *list.Model - font string - zipFolder string - families []string - spinner spinner.Model - state state - system bool + err error + list *list.Model + Asset + families []string + spinner spinner.Model + state state + system bool } func (m *main) buildFontList(nerdFonts []*Asset) { @@ -122,7 +121,7 @@ func downloadFontZip(location string) { } func installLocalFontZIP(m *main) { - data, err := os.ReadFile(m.font) + data, err := os.ReadFile(m.URL) if err != nil { program.Send(errMsg(err)) return @@ -143,28 +142,48 @@ func installFontZIP(zipFile []byte, m *main) { func (m *main) Init() tea.Cmd { isLocalZipFile := func() bool { - return !strings.HasPrefix(m.font, "https") && strings.HasSuffix(m.font, ".zip") + return !strings.HasPrefix(m.URL, "https") && strings.HasSuffix(m.URL, ".zip") } - if len(m.font) != 0 && !isLocalZipFile() { + resolveFontZipURL := func() error { + if strings.HasPrefix(m.URL, "https") { + return nil + } + + fonts, err := Fonts() + if err != nil { + return err + } + + var fontAsset *Asset + for _, font := range fonts { + if m.URL != font.Name { + continue + } + + fontAsset = font + break + } + + if fontAsset == nil { + return fmt.Errorf("no matching font found") + } + + m.Asset = *fontAsset + + return nil + } + + if len(m.URL) != 0 && !isLocalZipFile() { m.state = downloadFont - if !strings.HasPrefix(m.font, "https") { - if m.font == CascadiaCodeMS { - cascadia, err := CascadiaCode() - if err != nil { - m.err = err - return tea.Quit - } - - m.font = cascadia.URL - } else { - m.font = fmt.Sprintf("https://github.com/ryanoasis/nerd-fonts/releases/latest/download/%s.zip", m.font) - } + if err := resolveFontZipURL(); err != nil { + m.err = err + return tea.Quit } defer func() { - go downloadFontZip(m.font) + go downloadFontZip(m.URL) }() m.spinner.Spinner = spinner.Globe @@ -214,20 +233,25 @@ func (m *main) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Quit case "enter": - if len(m.font) != 0 || m.list == nil || m.list.SelectedItem() == nil { + if len(m.URL) != 0 || m.list == nil || m.list.SelectedItem() == nil { return m, nil } + var font *Asset var ok bool + if font, ok = m.list.SelectedItem().(*Asset); !ok { m.err = fmt.Errorf("no font selected") return m, tea.Quit } + m.state = downloadFont - m.font = font.Name + m.Asset = *font + defer func() { go downloadFontZip(font.URL) }() + m.spinner.Spinner = spinner.Globe return m, m.spinner.Tick @@ -295,11 +319,11 @@ func (m *main) View() string { case selectFont: return fmt.Sprintf("\n%s%s", m.list.View(), terminal.StopProgress()) case downloadFont: - return textStyle.Render(fmt.Sprintf("%s Downloading %s%s", m.spinner.View(), m.font, terminal.StartProgress())) + return textStyle.Render(fmt.Sprintf("%s Downloading %s%s", m.spinner.View(), m.Name, terminal.StartProgress())) case unzipFont: - return textStyle.Render(fmt.Sprintf("%s Extracting %s", m.spinner.View(), m.font)) + return textStyle.Render(fmt.Sprintf("%s Extracting %s", m.spinner.View(), m.Name)) case installFont: - return textStyle.Render(fmt.Sprintf("%s Installing %s", m.spinner.View(), m.font)) + return textStyle.Render(fmt.Sprintf("%s Installing %s", m.spinner.View(), m.Name)) case quit: return textStyle.Render(fmt.Sprintf("No need to install a new font? That's cool.%s", terminal.StopProgress())) case done: @@ -309,7 +333,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(fmt.Sprintf("Successfully installed %s 🚀\n\n%s", m.Name, terminal.StopProgress())) builder.WriteString("The following font families are now available for configuration:\n\n") for i, family := range m.families { @@ -328,9 +352,12 @@ func (m *main) View() string { func Run(font string, ch cache_.Cache, root bool, zipFolder string) { main := &main{ - font: font, - system: root, - zipFolder: zipFolder, + system: root, + Asset: Asset{ + Name: font, + URL: font, + Folder: zipFolder, + }, } cache = ch diff --git a/src/font/download.go b/src/font/download.go index 5c03fabc..8d3ebfbd 100644 --- a/src/font/download.go +++ b/src/font/download.go @@ -10,25 +10,55 @@ import ( "io" httplib "net/http" "net/url" + "os" + "path" + "path/filepath" + cache_ "github.com/jandedobbeleer/oh-my-posh/src/cache" "github.com/jandedobbeleer/oh-my-posh/src/runtime/http" ) -func Download(fontPath string) ([]byte, error) { - u, err := url.Parse(fontPath) +func Download(fontURL string) ([]byte, error) { + if zipPath, OK := cache.Get(fontURL); OK { + if b, err := os.ReadFile(zipPath); err == nil { + return b, nil + } + } + + // validate if we have a local file + u, err := url.Parse(fontURL) if err != nil || u.Scheme != "https" { return nil, errors.New("font path must be a valid URL") } var b []byte - if b, err = getRemoteFile(fontPath); err != nil { + if b, err = getRemoteFile(fontURL); err != nil { return nil, err } if !isZipFile(b) { - return nil, fmt.Errorf("%s is not a valid zip file", fontPath) + return nil, fmt.Errorf("%s is not a valid zip file", fontURL) } + fileName := path.Base(fontURL) + + zipPath := filepath.Join(os.TempDir(), fileName) + tempFile, err := os.Create(zipPath) + defer func() { + _ = tempFile.Close() + }() + + if err != nil { + return b, nil + } + + _, err = tempFile.Write(b) + if err != nil { + return b, nil + } + + cache.Set(fontURL, zipPath, cache_.ONEDAY) + return b, nil } @@ -42,10 +72,12 @@ func getRemoteFile(location string) (data []byte, err error) { if err != nil { return nil, err } + resp, err := http.HTTPClient.Do(req) if err != nil { return } + defer resp.Body.Close() if resp.StatusCode != httplib.StatusOK { diff --git a/src/font/fonts.go b/src/font/fonts.go index 637ce3a8..0cf08ef2 100644 --- a/src/font/fonts.go +++ b/src/font/fonts.go @@ -23,9 +23,10 @@ type release struct { } type Asset struct { - Name string `json:"name"` - URL string `json:"browser_download_url"` - State string `json:"state"` + Name string `json:"name"` + URL string `json:"browser_download_url"` + State string `json:"state"` + Folder string `json:"folder"` } func (a Asset) FilterValue() string { return a.Name } @@ -42,7 +43,7 @@ func Fonts() ([]*Asset, error) { cascadiaCode, err := CascadiaCode() if err == nil { - assets = append(assets, cascadiaCode) + assets = append(assets, cascadiaCode...) } sort.Slice(assets, func(i, j int) bool { return assets[i].Name < assets[j].Name }) @@ -84,16 +85,39 @@ func setCachedFontData(assets []*Asset) { cache.Set(cache_.FONTLISTCACHE, string(data), cache_.ONEDAY) } -func CascadiaCode() (*Asset, error) { +func CascadiaCode() ([]*Asset, error) { assets, err := fetchFontAssets("microsoft/cascadia-code") if err != nil || len(assets) != 1 { return nil, errors.New("no assets found") } - // patch the name - assets[0].Name = CascadiaCodeMS - - return assets[0], nil + return []*Asset{ + { + Name: fmt.Sprintf("%s - TTF", CascadiaCodeMS), + URL: assets[0].URL, + Folder: "ttf/", + }, + { + Name: fmt.Sprintf("%s - TTF Static", CascadiaCodeMS), + URL: assets[0].URL, + Folder: "ttf/static/", + }, + { + Name: fmt.Sprintf("%s - OTF Static", CascadiaCodeMS), + URL: assets[0].URL, + Folder: "otf/static/", + }, + { + Name: fmt.Sprintf("%s - WOFF2", CascadiaCodeMS), + URL: assets[0].URL, + Folder: "woff2/static/", + }, + { + Name: fmt.Sprintf("%s - WOFF2 Static", CascadiaCodeMS), + URL: assets[0].URL, + Folder: "woff2/static/", + }, + }, nil } func fetchFontAssets(repo string) ([]*Asset, error) { diff --git a/src/font/install.go b/src/font/install.go index d23c657c..a7dbee50 100644 --- a/src/font/install.go +++ b/src/font/install.go @@ -45,7 +45,7 @@ func InstallZIP(data []byte, m *main) ([]string, error) { } fontFileName := path.Base(file.Name) - fontRelativeFileName := strings.TrimPrefix(file.Name, m.zipFolder) + fontRelativeFileName := strings.TrimPrefix(file.Name, m.Folder) // do not install fonts that are not in the specified installation folder if fontFileName != fontRelativeFileName { diff --git a/src/image/image.go b/src/image/image.go index b97c4cb8..9580d206 100644 --- a/src/image/image.go +++ b/src/image/image.go @@ -36,7 +36,7 @@ import ( "unicode/utf8" "github.com/jandedobbeleer/oh-my-posh/src/cache" - fontCLI "github.com/jandedobbeleer/oh-my-posh/src/font" + font_ "github.com/jandedobbeleer/oh-my-posh/src/font" "github.com/jandedobbeleer/oh-my-posh/src/regex" "github.com/jandedobbeleer/oh-my-posh/src/runtime" "github.com/jandedobbeleer/oh-my-posh/src/terminal" @@ -229,7 +229,7 @@ func (ir *Renderer) loadFonts() error { url := "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/Hack.zip" var err error - data, err = fontCLI.Download(url) + data, err = font_.Download(url) if err != nil { return err } diff --git a/website/docs/installation/fonts.mdx b/website/docs/installation/fonts.mdx index ffe8e9ba..a73f2611 100644 --- a/website/docs/installation/fonts.mdx +++ b/website/docs/installation/fonts.mdx @@ -47,12 +47,6 @@ This will present a list of Nerd Font libraries, from which you can select `Mes oh-my-posh font install meslo ``` -If you have a font that has specific flavors of a font inside sub folders, you can specify the sub folder name: - -```bash -oh-my-posh font install --zip-folder ttf/static "CascadiaCode (MS)" -``` - From bba8c040c263a3255c82aa232e5a93fd6683a7ec Mon Sep 17 00:00:00 2001 From: Jan De Dobbeleer Date: Fri, 20 Dec 2024 09:05:48 +0100 Subject: [PATCH 3/3] fix(cli): open EDITOR correctly resolves #6019 --- src/cli/edit.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/cli/edit.go b/src/cli/edit.go index f03227cc..eb2c1a39 100644 --- a/src/cli/edit.go +++ b/src/cli/edit.go @@ -14,21 +14,22 @@ func editFileWithEditor(file string) int { return 1 } - var args []string - if strings.Contains(editor, " ") { - strs := strings.Split(editor, " ") - editor = strs[0] - args = strs[1:] - } + editor = strings.TrimSpace(editor) + args := strings.Split(editor, " ") + + editor = args[0] + args = append(args[1:], file) - args = append(args, file) cmd := exec.Command(editor, args...) - err := cmd.Run() - if err != nil { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { fmt.Println(err.Error()) return 1 } - return cmd.ProcessState.ExitCode() + return 0 }