package main import ( // "encoding/json" "bytes" json2 "encoding/json" "errors" "fmt" "os" "strconv" "strings" "github.com/gookit/config/v2" "github.com/gookit/config/v2/json" "github.com/gookit/config/v2/toml" "github.com/gookit/config/v2/yaml" "github.com/mitchellh/mapstructure" ) // Config holds all the theme for rendering the prompt type Config struct { FinalSpace bool `config:"final_space"` OSC99 bool `config:"osc99"` ConsoleTitle bool `config:"console_title"` ConsoleTitleStyle ConsoleTitleStyle `config:"console_title_style"` ConsoleTitleTemplate string `config:"console_title_template"` TerminalBackground string `config:"terminal_background"` Blocks []*Block `config:"blocks"` Tooltips []*Segment `config:"tooltips"` TransientPrompt *TransientPrompt `config:"transient_prompt"` Palette Palette `config:"palette"` } type TransientPrompt struct { Template string `config:"template"` Background string `config:"background"` Foreground string `config:"foreground"` } const ( // EnableHyperlink enable hyperlink EnableHyperlink Property = "enable_hyperlink" // HTTPTimeout timeout used when executing http request HTTPTimeout Property = "http_timeout" // DefaultHTTPTimeout default timeout used when executing http request DefaultHTTPTimeout = 20 // DefaultCacheTimeout default timeout used when caching data DefaultCacheTimeout = 10 ) func printConfigError(err error, eval bool) { if eval { fmt.Println("echo \"Oh My Posh Error:\n\"", err.Error()) return } fmt.Println("Oh My Posh Error:\n", err.Error()) } // GetConfig returns the default configuration including possible user overrides func GetConfig(env environmentInfo) *Config { cfg, err := loadConfig(env) if err != nil { return getDefaultConfig(err.Error()) } return cfg } func loadConfig(env environmentInfo) (*Config, error) { var cfg Config configFile := *env.getArgs().Config eval := *env.getArgs().Eval if configFile == "" { return nil, errors.New("NO CONFIG") } if _, err := os.Stat(configFile); os.IsNotExist(err) { printConfigError(err, eval) return nil, errors.New("INVALID CONFIG PATH") } config.AddDriver(yaml.Driver) config.AddDriver(json.Driver) config.AddDriver(toml.Driver) config.WithOptions(func(opt *config.Options) { opt.DecoderConfig = &mapstructure.DecoderConfig{ TagName: "config", } }) err := config.LoadFiles(configFile) if err != nil { printConfigError(err, eval) return nil, errors.New("UNABLE TO OPEN CONFIG") } err = config.BindStruct("", &cfg) if err != nil { printConfigError(err, eval) return nil, errors.New("INVALID CONFIG") } // initialize default values if cfg.TransientPrompt == nil { cfg.TransientPrompt = &TransientPrompt{} } return &cfg, nil } func exportConfig(configFile, format string) string { if len(format) == 0 { format = config.JSON } config.AddDriver(yaml.Driver) config.AddDriver(json.Driver) config.AddDriver(toml.Driver) err := config.LoadFiles(configFile) if err != nil { printConfigError(err, false) return fmt.Sprintf("INVALID CONFIG:\n\n%s", err.Error()) } schemaKey := "$schema" if format == config.JSON && !config.Exists(schemaKey) { data := config.Data() data[schemaKey] = "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json" config.SetData(data) } buf := new(bytes.Buffer) _, err = config.DumpTo(buf, format) if err != nil { printConfigError(err, false) return "UNABLE TO DUMP CONFIG" } switch format { case config.JSON: var prettyJSON bytes.Buffer err := json2.Indent(&prettyJSON, buf.Bytes(), "", " ") if err == nil { unescapeUnicodeCharactersInJSON := func(rawJSON []byte) string { str, err := strconv.Unquote(strings.ReplaceAll(strconv.Quote(string(rawJSON)), `\\u`, `\u`)) if err != nil { return err.Error() } return str } return unescapeUnicodeCharactersInJSON(prettyJSON.Bytes()) } case config.Yaml: prefix := "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n" content := buf.String() return prefix + content case config.Toml: prefix := "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n" content := buf.String() return prefix + content } return buf.String() } func getDefaultConfig(info string) *Config { cfg := &Config{ FinalSpace: true, ConsoleTitle: true, ConsoleTitleStyle: FolderName, Blocks: []*Block{ { Type: Prompt, Alignment: Left, Segments: []*Segment{ { Type: Session, Style: Diamond, Background: "#c386f1", Foreground: "#ffffff", LeadingDiamond: "\uE0B6", TrailingDiamond: "\uE0B0", }, { Type: Path, Style: Powerline, PowerlineSymbol: "\uE0B0", Background: "#ff479c", Foreground: "#ffffff", Properties: map[Property]interface{}{ Prefix: " \uE5FF ", Style: "folder", }, }, { Type: Git, Style: Powerline, PowerlineSymbol: "\uE0B0", Background: "#fffb38", Foreground: "#193549", Properties: map[Property]interface{}{ FetchStashCount: true, FetchUpstreamIcon: true, }, }, { Type: Battery, Style: Powerline, PowerlineSymbol: "\uE0B0", Background: "#f36943", Foreground: "#193549", Properties: map[Property]interface{}{ ColorBackground: true, ChargedColor: "#4caf50", ChargingColor: "#40c4ff", DischargingColor: "#ff5722", Postfix: "\uF295 ", }, }, { Type: Node, Style: Powerline, PowerlineSymbol: "\uE0B0", Background: "#6CA35E", Foreground: "#ffffff", Properties: map[Property]interface{}{ Prefix: " \uE718", DisplayVersion: false, }, }, { Type: ShellInfo, Style: Powerline, PowerlineSymbol: "\uE0B0", Background: "#0077c2", Foreground: "#ffffff", Properties: map[Property]interface{}{ Prefix: " \uFCB5 ", }, }, { Type: Root, Style: Powerline, PowerlineSymbol: "\uE0B0", Background: "#ffff66", Foreground: "#ffffff", }, { Type: Text, Style: Powerline, PowerlineSymbol: "\uE0B0", Background: "#ffffff", Foreground: "#111111", Properties: map[Property]interface{}{ TextProperty: info, }, }, { Type: Exit, Style: Diamond, Background: "#2e9599", Foreground: "#ffffff", LeadingDiamond: "\uE0B0", TrailingDiamond: "\uE0B4", Properties: map[Property]interface{}{ DisplayExitCode: false, AlwaysEnabled: true, ErrorColor: "#f1184c", ColorBackground: true, Prefix: " \uE23A", }, }, }, }, }, } return cfg }