feat(cli): install font

relates to #2338
This commit is contained in:
Jan De Dobbeleer 2022-06-02 11:30:41 +02:00 committed by Jan De Dobbeleer
parent 1b8185cb86
commit 1fba9d563c
12 changed files with 803 additions and 3 deletions

131
src/cli/font.go Normal file
View file

@ -0,0 +1,131 @@
package cli
import (
"fmt"
"oh-my-posh/font"
"github.com/spf13/cobra"
)
// fontCmd can work with fonts
var fontCmd = &cobra.Command{
Use: "font [install|configure]",
Short: "Manage fonts",
Long: `Manage fonts.
This command is used to install fonts and configure the font in your terminal.
- install: oh-my-posh install font https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/3270.zip`,
ValidArgs: []string{
"install",
"configure",
},
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
_ = cmd.Help()
return
}
switch args[0] {
case "install":
font.Run()
case "configure":
fmt.Println("not implemented")
default:
_ = cmd.Help()
}
},
}
func init() { // nolint:gochecknoinits
rootCmd.AddCommand(fontCmd)
}
// type fontsModel struct {
// fonts []*font.Asset // the list of choices
// cursor int // which item our cursor is pointing at
// selected map[int]*font.Asset // which items are selected
// }
// func initFontsModel() (*fontsModel, error) {
// nerdFonts, err := font.Nerds()
// if err != nil {
// return nil, err
// }
// return &fontsModel{
// fonts: nerdFonts,
// selected: make(map[int]*font.Asset),
// }, nil
// }
// func (f fontsModel) Init() tea.Cmd {
// // Just return `nil`, which means "no I/O right now, please."
// return nil
// }
// func (f fontsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// switch msg := msg.(type) {
// // Is it a key press?
// case tea.KeyMsg:
// // Cool, what was the actual key pressed?
// switch msg.String() {
// // These keys should exit the program.
// case "ctrl+c", "q":
// return f, tea.Quit
// // The "up" and "k" keys move the cursor up
// case "up", "k":
// if f.cursor > 0 {
// f.cursor--
// }
// // The "down" and "j" keys move the cursor down
// case "down", "j":
// if f.cursor < len(f.fonts)-1 {
// f.cursor++
// }
// // The "enter" key and the spacebar (a literal space) toggle
// // the selected state for the item that the cursor is pointing at.
// case "enter", " ":
// _, ok := f.selected[f.cursor]
// if ok {
// delete(f.selected, f.cursor)
// } else {
// f.selected[f.cursor] = f.fonts[f.cursor]
// }
// }
// }
// // Return the updated model to the Bubble Tea runtime for processing.
// // Note that we're not returning a command.
// return f, nil
// }
// func (f fontsModel) View() string {
// // The header
// s := "Which font do you want to install?\n\n"
// // Iterate over our choices
// for i, choice := range f.fonts {
// // Is the cursor pointing at this choice?
// cursor := " " // no cursor
// if f.cursor == i {
// cursor = ">" // cursor!
// }
// // Is this choice selected?
// checked := " " // not selected
// if _, ok := f.selected[i]; ok {
// checked = "x" // selected!
// }
// // Render the row
// s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice.Name)
// }
// // The footer
// s += "\nPress q to quit.\n"
// // Send the UI for rendering
// return s
// }

228
src/font/cli.go Normal file
View file

@ -0,0 +1,228 @@
package font
import (
"fmt"
"io"
"os"
"github.com/charmbracelet/bubbles/list"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
var program *tea.Program
const listHeight = 14
var (
titleStyle = lipgloss.NewStyle().MarginLeft(2)
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(lipgloss.Color("170"))
paginationStyle = list.DefaultStyles().PaginationStyle.PaddingLeft(4)
helpStyle = lipgloss.NewStyle().PaddingLeft(4).PaddingBottom(1)
textStyle = lipgloss.NewStyle().Margin(1, 0, 2, 4)
)
type loadMsg []*Asset
type zipMsg []byte
type successMsg int
type errMsg error
type state int
type itemDelegate struct{}
func (d itemDelegate) Height() int { return 1 }
func (d itemDelegate) Spacing() int { return 0 }
func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }
func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) { //nolint: gocritic
i, ok := listItem.(*Asset)
if !ok {
return
}
str := fmt.Sprintf(i.Name)
fn := itemStyle.Render
if index == m.Index() {
fn = func(s string) string {
return selectedItemStyle.Render("> " + s)
}
}
fmt.Fprint(w, fn(str))
}
const (
getFonts state = iota
selectFont
downloadFont
unzipFont
installFont
quit
done
)
type main struct {
spinner spinner.Model
list *list.Model
fontname string
state state
err error
}
func (m *main) buildFontList(nerdFonts []*Asset) {
var items []list.Item
for _, font := range nerdFonts {
items = append(items, font)
}
const defaultWidth = 20
l := list.New(items, itemDelegate{}, defaultWidth, listHeight)
l.Title = "Which font do you want to install?"
l.SetShowStatusBar(false)
l.SetFilteringEnabled(true)
l.Styles.Title = titleStyle
l.Styles.PaginationStyle = paginationStyle
l.Styles.HelpStyle = helpStyle
m.list = &l
}
func getFontsList() {
nerdFonts, err := Nerds()
if err != nil {
program.Send(errMsg(err))
return
}
program.Send(loadMsg(nerdFonts))
}
func downloadFontZip(location string) {
zipFile, err := Download(location)
if err != nil {
program.Send(errMsg(err))
return
}
program.Send(zipMsg(zipFile))
}
func installFontZIP(zipFile []byte) {
err := InstallZIP(zipFile)
if err != nil {
program.Send(errMsg(err))
return
}
program.Send(successMsg(0))
}
func (m *main) Init() tea.Cmd {
go getFontsList()
return m.spinner.Tick
}
func (m *main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case loadMsg:
m.state = selectFont
m.buildFontList(msg)
return m, nil
case tea.WindowSizeMsg:
if m.list == nil {
return m, nil
}
m.list.SetWidth(msg.Width)
return m, nil
case tea.KeyMsg:
switch keypress := msg.String(); keypress {
case "ctrl+c", "q", "esc":
m.state = quit
return m, tea.Quit
case "enter":
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.fontname = font.Name
go downloadFontZip(font.URL)
m.spinner.Spinner = spinner.Globe
return m, m.spinner.Tick
}
case zipMsg:
m.state = installFont
go installFontZIP(msg)
m.spinner.Spinner = spinner.Dot
return m, m.spinner.Tick
case successMsg:
m.state = done
return m, tea.Quit
case errMsg:
m.err = msg
return m, tea.Quit
case spinner.TickMsg:
var cmd tea.Cmd
m.spinner, cmd = m.spinner.Update(msg)
return m, cmd
default:
return m, nil
}
lst, cmd := m.list.Update(msg)
m.list = &lst
return m, cmd
}
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()))
case selectFont:
return "\n" + m.list.View()
case downloadFont:
return textStyle.Render(fmt.Sprintf("%s Downloading %s", m.spinner.View(), m.fontname))
case unzipFont:
return textStyle.Render(fmt.Sprintf("%s Extracting %s", m.spinner.View(), m.fontname))
case installFont:
return textStyle.Render(fmt.Sprintf("%s Installing %s", m.spinner.View(), m.fontname))
case quit:
return textStyle.Render("No need to install a new font? Thats cool.")
case done:
return textStyle.Render(fmt.Sprintf("Successfully installed %s 🚀", m.fontname))
}
return ""
}
func Run() {
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("170"))
m := &main{
spinner: s,
state: getFonts,
}
program = tea.NewProgram(m)
if err := program.Start(); err != nil {
print("Error running program: %v", err)
os.Exit(1)
}
}

57
src/font/download.go Normal file
View file

@ -0,0 +1,57 @@
package font
// A simple program demonstrating the spinner component from the Bubbles
// component library.
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
)
func Download(fontPath string) ([]byte, error) {
u, err := url.Parse(fontPath)
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 {
return nil, err
}
if !isZipFile(b) {
return nil, fmt.Errorf("%s is not a valid zip file", fontPath)
}
return b, nil
}
func isZipFile(data []byte) bool {
contentType := http.DetectContentType(data)
return contentType == "application/zip"
}
func getRemoteFile(location string) (data []byte, err error) {
print("Downloading %s", location)
var client = http.Client{}
resp, err := client.Get(location)
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return
}
data, err = io.ReadAll(resp.Body)
if err != nil {
return
}
return
}

76
src/font/font.go Normal file
View file

@ -0,0 +1,76 @@
// Derived from https://github.com/Crosse/font-install
// Copyright 2020 Seth Wright <seth@crosse.org>
package font
import (
"bytes"
"fmt"
"path"
"strings"
"github.com/ConradIrwin/font/sfnt"
)
// Font describes a font file and the various metadata associated with it.
type Font struct {
Name string
Family string
FileName string
Metadata map[sfnt.NameID]string
Data []byte
}
// fontExtensions is a list of file extensions that denote fonts.
// Only files ending with these extensions will be installed.
var fontExtensions = map[string]bool{
".otf": true,
".ttf": true,
}
// new creates a new Font struct.
// fileName is the font's file name, and data is a byte slice containing the font file data.
// It returns a FontData struct describing the font, or an error.
func new(fileName string, data []byte) (*Font, error) {
if _, ok := fontExtensions[strings.ToLower(path.Ext(fileName))]; !ok {
return nil, fmt.Errorf("not a font: %v", fileName)
}
font := &Font{
FileName: fileName,
Metadata: make(map[sfnt.NameID]string),
Data: data,
}
fontData, err := sfnt.Parse(bytes.NewReader(font.Data))
if err != nil {
return nil, err
}
if !fontData.HasTable(sfnt.TagName) {
return nil, fmt.Errorf("font %v has no name table", fileName)
}
nameTable, err := fontData.NameTable()
if err != nil {
return nil, err
}
for _, nameEntry := range nameTable.List() {
font.Metadata[nameEntry.NameID] = nameEntry.String()
}
font.Name = font.Metadata[sfnt.NameFull]
font.Family = font.Metadata[sfnt.NamePreferredFamily]
if len(font.Family) == 0 {
if v, ok := font.Metadata[sfnt.NameFontFamily]; ok {
font.Family = v
}
}
if len(font.Name) == 0 {
font.Name = fileName
}
return font, nil
}

75
src/font/install.go Normal file
View file

@ -0,0 +1,75 @@
// Derived from https://github.com/Crosse/font-install
// Copyright 2020 Seth Wright <seth@crosse.org>
package font
import (
"archive/zip"
"bytes"
"io"
"path"
"runtime"
"strings"
)
func InstallZIP(data []byte) (err error) {
bytesReader := bytes.NewReader(data)
zipReader, err := zip.NewReader(bytesReader, int64(bytesReader.Len()))
if err != nil {
return
}
fonts := make(map[string]*Font)
for _, zf := range zipReader.File {
rc, err := zf.Open()
if err != nil {
return err
}
defer rc.Close()
data, err := io.ReadAll(rc)
if err != nil {
return err
}
fontData, err := new(zf.Name, data)
if err != nil {
continue
}
if _, ok := fonts[fontData.Name]; !ok {
fonts[fontData.Name] = fontData
} else {
// Prefer OTF over TTF; otherwise prefer the first font we found.
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
}
}
}
for _, font := range fonts {
if !shouldInstall(font.Name) {
continue
}
// print("Installing %s", font.Name)
if err = install(font); err != nil {
return err
}
}
return nil
}
func shouldInstall(name string) bool {
name = strings.ToLower(name)
switch runtime.GOOS {
case "windows":
return strings.Contains(name, "windows compatible")
default:
return !strings.Contains(name, "windows compatible")
}
}

View file

@ -0,0 +1,25 @@
// Derived from https://github.com/Crosse/font-install
// Copyright 2020 Seth Wright <seth@crosse.org>
package font
import (
"os"
"path"
)
var FontsDir = path.Join(os.Getenv("HOME"), "Library", "Fonts")
func install(font *Font) (err error) {
// On darwin/OSX, the user's fonts directory is ~/Library/Fonts,
// and fonts should be installed directly into that path;
// i.e., not in subfolders.
fullPath := path.Join(FontsDir, path.Base(font.FileName))
if err = os.MkdirAll(path.Dir(fullPath), 0700); err != nil {
return
}
err = os.WriteFile(fullPath, font.Data, 0644)
return
}

31
src/font/install_unix.go Normal file
View file

@ -0,0 +1,31 @@
//go:build linux
// Derived from https://github.com/Crosse/font-install
// Copyright 2020 Seth Wright <seth@crosse.org>
package font
import (
"io/ioutil"
"os"
"path"
"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")
func install(font *Font) (err error) {
// 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,
strings.ToLower(strings.ReplaceAll(font.Family, " ", "-")),
path.Base(font.FileName))
if err = os.MkdirAll(path.Dir(fullPath), 0700); err != nil {
return err
}
return ioutil.WriteFile(fullPath, font.Data, 0644)
}

View file

@ -0,0 +1,58 @@
// Derived from https://github.com/Crosse/font-install
// Copyright 2020 Seth Wright <seth@crosse.org>
package font
import (
"fmt"
"io/ioutil"
"os"
"path"
"golang.org/x/sys/windows/registry"
)
// FontsDir denotes the path to the user's fonts directory on Linux.
// Windows doesn't have the concept of a permanent, per-user collection
// of fonts, meaning that all fonts are stored in the system-level fonts
// directory, which is %WINDIR%\Fonts by default.
var FontsDir = path.Join(os.Getenv("WINDIR"), "Fonts")
func install(font *Font) (err error) {
// To install a font on Windows:
// - Copy the file to the fonts directory
// - Create a registry entry for the font
fullPath := path.Join(FontsDir, font.FileName)
err = ioutil.WriteFile(fullPath, font.Data, 0644) //nolint:gosec
if err != nil {
return err
}
// Second, write metadata about the font to the registry.
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, registry.WRITE)
if err != nil {
// If this fails, remove the font file as well.
if nexterr := os.Remove(fullPath); nexterr != nil {
return nexterr
}
return err
}
defer k.Close()
// Apparently it's "ok" to mark an OpenType font as "TrueType",
// and since this tool only supports True- and OpenType fonts,
// this should be Okay(tm).
// Besides, Windows does it, so why can't I?
valueName := fmt.Sprintf("%v (TrueType)", font.FileName)
if err = k.SetStringValue(font.Name, valueName); err != nil {
// If this fails, remove the font file as well.
if nexterr := os.Remove(fullPath); nexterr != nil {
return nexterr
}
return err
}
return nil
}

47
src/font/nerd.go Normal file
View file

@ -0,0 +1,47 @@
package font
import (
"encoding/json"
"errors"
"net/http"
"strings"
)
type release struct {
Assets []*Asset `json:"assets"`
}
type Asset struct {
Name string `json:"name"`
URL string `json:"browser_download_url"`
State string `json:"state"`
}
func (a Asset) FilterValue() string { return a.Name }
func Nerds() ([]*Asset, error) {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.github.com/repos/ryanoasis/nerd-fonts/releases/latest", nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "application/vnd.github.v3+json")
response, err := client.Do(req)
if err != nil || response.StatusCode != http.StatusOK {
return nil, errors.New("failed to get nerd fonts release")
}
defer response.Body.Close()
var release release
err = json.NewDecoder(response.Body).Decode(&release)
if err != nil {
return nil, errors.New("failed to parse nerd fonts release")
}
var nerdFonts []*Asset
for _, asset := range release.Assets {
if asset.State == "uploaded" && strings.HasSuffix(asset.Name, ".zip") {
asset.Name = strings.TrimSuffix(asset.Name, ".zip")
nerdFonts = append(nerdFonts, asset)
}
}
return nerdFonts, nil
}

View file

@ -33,6 +33,10 @@ require (
)
require (
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732
github.com/charmbracelet/bubbles v0.11.0
github.com/charmbracelet/bubbletea v0.21.0
github.com/charmbracelet/lipgloss v0.5.0
github.com/hashicorp/hcl/v2 v2.12.0
github.com/spf13/cobra v1.4.0
golang.org/x/mod v0.5.1
@ -62,16 +66,29 @@ require (
)
require (
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.0 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
github.com/zclconf/go-cty v1.10.0 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
)
replace github.com/distatus/battery v0.10.0 => github.com/JanDeDobbeleer/battery v0.10.0-5

View file

@ -1,7 +1,11 @@
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab h1:Ew70NL+wL6v9looOiJJthlqA41VzoJS+q9AyjHJe6/g=
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab/go.mod h1:FvHgTMJanm43G7B3MVSjS/jim5ytVqAJNAOpRhnuHJc=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732 h1:0EDePskeF4vNFCk70ATaFHQzjmwXsk+VImnMJttecNU=
github.com/ConradIrwin/font v0.0.0-20210318200717-ce8d41cc0732/go.mod h1:krTLO7JWu6g8RMxG8sl+T1Hf8W93XQacBKJmqFZ2MFY=
github.com/JanDeDobbeleer/battery v0.10.0-5 h1:RdZlM4ioJRVrt0JFAgLASbECb7xlBifvmgwYFdwp55I=
github.com/JanDeDobbeleer/battery v0.10.0-5/go.mod h1:STnSvFLX//eEpkaN7qWRxCWxrWOcssTDgnG4yqq9BRE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@ -23,10 +27,24 @@ github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:o
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/charmbracelet/bubbles v0.11.0 h1:fBLyY0PvJnd56Vlu5L84JJH6f4axhgIJ9P3NET78f0Q=
github.com/charmbracelet/bubbles v0.11.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
github.com/charmbracelet/bubbletea v0.21.0 h1:f3y+kanzgev5PA916qxmDybSHU3N804uOnKnhRPXTcI=
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8=
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/cpuguy83/go-md2man/v2 v2.0.1/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=
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/esimov/stackblur-go v1.1.0 h1:fwnZJC/7sHFzu4CDMgdJ1QxMN/q3k5MGILuoU4hH6oQ=
github.com/esimov/stackblur-go v1.1.0/go.mod h1:7PcTPCHHKStxbZvBkUlQJjRclqjnXtQ0NoORZt1AlHE=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
@ -74,18 +92,27 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
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=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
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-20220326011226-f1430873d8db h1:m2s7Fwo4OwmcheIWUc/Nw9/MZ0eFtP3to0ovTpqOiCQ=
github.com/lufia/plan9stats v0.0.0-20220326011226-f1430873d8db/go.mod h1:VgrrWVwBO2+6XKn8ypT3WUqvoxCa8R2M5to2tRzGovI=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/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=
@ -105,13 +132,28 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.0 h1:SOpr+CfyVNce341kKqvbhhzQhBPyJRXQaCtn03Pae1Q=
github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI=
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
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-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI=
github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@ -120,6 +162,8 @@ github.com/shirou/gopsutil/v3 v3.22.4/go.mod h1:D01hZJ4pVHPpCTZ3m3T2+wDF2YAGfd+H
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/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.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
@ -142,6 +186,7 @@ github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03O
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
@ -179,14 +224,20 @@ golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View file

@ -11,9 +11,13 @@ We recommend [Meslo LGM NF][meslo], but any Nerd Font should be compatible with
To see the icons displayed in Oh My Posh, **install** a [Nerd Font][nerdfonts], and **configure** your terminal to use it.
#### Windows
#### Installation
Download your chosen Nerd Font, and install the font system-wide. See this [thread][font-thread] for more context.
Oh My Posh has a CLI to help you select and install a [Nerd Font][nerdfonts] (beta):
```bash
oh-my-posh font install
```
#### Windows Terminal