Skip to content

Commit

Permalink
feat(config): use custom config parser
Browse files Browse the repository at this point in the history
  • Loading branch information
JanDeDobbeleer committed Mar 11, 2024
1 parent 8e0a4c1 commit 87d1719
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 187 deletions.
4 changes: 2 additions & 2 deletions src/ansi/ansi_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ type style struct {
}

type Colors struct {
Background string `json:"background"`
Foreground string `json:"foreground"`
Background string `json:"background" toml:"background"`
Foreground string `json:"foreground" toml:"foreground"`
}

const (
Expand Down
4 changes: 2 additions & 2 deletions src/ansi/palettes.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ansi

type Palettes struct {
Template string `json:"template,omitempty"`
List map[string]Palette `json:"list,omitempty"`
Template string `json:"template,omitempty" toml:"template,omitempty"`
List map[string]Palette `json:"list,omitempty" toml:"list,omitempty"`
}
20 changes: 10 additions & 10 deletions src/engine/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@ const (

// Block defines a part of the prompt with optional segments
type Block struct {
Type BlockType `json:"type,omitempty"`
Alignment BlockAlignment `json:"alignment,omitempty"`
Segments []*Segment `json:"segments,omitempty"`
Newline bool `json:"newline,omitempty"`
Filler string `json:"filler,omitempty"`
Overflow Overflow `json:"overflow,omitempty"`
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"`
VerticalOffset int `json:"vertical_offset,omitempty"`
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"`
MinWidth int `json:"min_width,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
Expand Down
178 changes: 87 additions & 91 deletions src/engine/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,23 @@ package engine

import (
"bytes"
json2 "encoding/json"
"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"

"github.com/gookit/config/v2"
"github.com/gookit/config/v2/json"
"github.com/gookit/config/v2/toml"
yaml "github.com/gookit/config/v2/yamlv3"
"github.com/mitchellh/mapstructure"
toml "github.com/pelletier/go-toml/v2"
)

const (
Expand All @@ -34,33 +31,33 @@ const (

// Config holds all the theme for rendering the prompt
type Config struct {
Version int `json:"version"`
FinalSpace bool `json:"final_space,omitempty"`
ConsoleTitleTemplate string `json:"console_title_template,omitempty"`
TerminalBackground string `json:"terminal_background,omitempty"`
AccentColor string `json:"accent_color,omitempty"`
Blocks []*Block `json:"blocks,omitempty"`
Tooltips []*Segment `json:"tooltips,omitempty"`
TransientPrompt *Segment `json:"transient_prompt,omitempty"`
ValidLine *Segment `json:"valid_line,omitempty"`
ErrorLine *Segment `json:"error_line,omitempty"`
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty"`
DebugPrompt *Segment `json:"debug_prompt,omitempty"`
Palette ansi.Palette `json:"palette,omitempty"`
Palettes *ansi.Palettes `json:"palettes,omitempty"`
Cycle ansi.Cycle `json:"cycle,omitempty"`
ShellIntegration bool `json:"shell_integration,omitempty"`
PWD string `json:"pwd,omitempty"`
Var map[string]any `json:"var,omitempty"`
DisableCursorPositioning bool `json:"disable_cursor_positioning,omitempty"`
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty"`
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"`

// Deprecated
OSC99 bool `json:"osc99,omitempty"`
OSC99 bool `json:"osc99,omitempty" toml:"osc99,omitempty"`

Output string `json:"-"`
MigrateGlyphs bool `json:"-"`
Format string `json:"-"`
Output string `json:"-" toml:"-"`
MigrateGlyphs bool `json:"-" toml:"-"`
Format string `json:"-" toml:"-"`

origin string
// eval bool
Expand Down Expand Up @@ -135,94 +132,82 @@ func loadConfig(env platform.Environment) *Config {
cfg.Format = strings.TrimPrefix(filepath.Ext(configFile), ".")
cfg.env = env

// support different extensions
// read the data
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":
case "yml", "yaml":
cfg.Format = YAML
case "jsonc":
err = yaml.Unmarshal(data, &cfg)
case "jsonc", "json":
cfg.Format = JSON
}

config.AddDriver(yaml.Driver.WithAliases("yaml", "yml"))
config.AddDriver(json.Driver.WithAliases("json", "jsonc"))
config.AddDriver(toml.Driver)

if config.Default().IsEmpty() {
config.WithOptions(func(opt *config.Options) {
opt.DecoderConfig = &mapstructure.DecoderConfig{
TagName: "json",
}
})
}
if cfg.Format == "jsonc" {
str := jsonutil.StripComments(string(data))
data = []byte(str)
}

err := config.LoadFiles(configFile)
if err != nil {
env.Error(err)
return defaultConfig(env, true)
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)
}

err = config.BindStruct("", &cfg)
if err != nil {
env.Error(err)
env.DebugF("error decoding config file: %s", err)
return defaultConfig(env, true)
}

return &cfg
}

func (cfg *Config) sync() {
if !cfg.updated {
return
}
var structMap map[string]any
inrec, err := json2.Marshal(cfg)
if err != nil {
return
}
err = json2.Unmarshal(inrec, &structMap)
if err != nil {
return
}
// remove empty structs
for k, v := range structMap {
if smap, OK := v.(map[string]any); OK && len(smap) == 0 {
delete(structMap, k)
}
}
config.SetData(structMap)
}

func (cfg *Config) Export(format string) string {
cfg.sync()

if len(format) != 0 {
cfg.Format = format
}

config.AddDriver(yaml.Driver)
config.AddDriver(toml.Driver)

var result bytes.Buffer

if cfg.Format == JSON {
jsonEncoder := json2.NewEncoder(&result)
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 + escapeGlyphs(result.String(), cfg.MigrateGlyphs)
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)
}

_, _ = config.DumpTo(&result, cfg.Format)
var prefix string
switch cfg.Format {
case YAML:
prefix = "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
case TOML:
prefix = "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
prefix := "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
tomlEncoder := toml.NewEncoder(&result)

err := tomlEncoder.Encode(cfg)
if err != nil {
return ""
}

return prefix + escapeGlyphs(result.String(), cfg.MigrateGlyphs)
}
return prefix + escapeGlyphs(result.String(), cfg.MigrateGlyphs)

// unsupported format
return ""
}

func (cfg *Config) BackupAndMigrate() {
Expand All @@ -233,19 +218,30 @@ func (cfg *Config) BackupAndMigrate() {

func (cfg *Config) Write(format string) {
content := cfg.Export(format)
if len(content) == 0 {
// we are unable to perform the export
os.Exit(1)
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, 0755)
if err != nil {
return
}

defer func() {
_ = f.Close()
}()

_, err = f.WriteString(content)
if err != nil {
return
}
_ = f.Close()
}

func (cfg *Config) Backup() {
Expand Down
34 changes: 0 additions & 34 deletions src/engine/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,12 @@ import (
"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/jandedobbeleer/oh-my-posh/src/segments"

"github.com/gookit/config/v2"
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)

func testClearDefaultConfig() {
config.Default().ClearAll()
}

func TestParseMappedLocations(t *testing.T) {
defer testClearDefaultConfig()
cases := []struct {
Case string
JSON string
}{
{Case: "new format", JSON: `{ "properties": { "mapped_locations": {"folder1": "one","folder2": "two"} } }`},
{Case: "old format", JSON: `{ "properties": { "mapped_locations": [["folder1", "one"], ["folder2", "two"]] } }`},
}
for _, tc := range cases {
config.ClearAll()
config.WithOptions(func(opt *config.Options) {
opt.DecoderConfig = &mapstructure.DecoderConfig{
TagName: "config",
}
})
err := config.LoadStrings(config.JSON, tc.JSON)
assert.NoError(t, err)
var segment Segment
err = config.BindStruct("", &segment)
assert.NoError(t, err)
mappedLocations := segment.Properties.GetKeyValueMap(segments.MappedLocations, make(map[string]string))
assert.Equal(t, "two", mappedLocations["folder2"])
}
}

func TestEscapeGlyphs(t *testing.T) {
defer testClearDefaultConfig()
cases := []struct {
Input string
Expected string
Expand Down
1 change: 0 additions & 1 deletion src/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ func engineRender() {
defer env.Close()

cfg := LoadConfig(env)
defer testClearDefaultConfig()

writerColors := cfg.MakeColors()
writer := &ansi.Writer{
Expand Down
Loading

0 comments on commit 87d1719

Please sign in to comment.