mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-21 12:16:54 +02:00
Previously we only filtered them out from the example config section in Config.md, but they still appeared in the schema. This is not ideal, because the schema descriptions can appear in editors on mouse hover or in auto-completions. So filter them out earlier, so that they don't appear in the schema either.
169 lines
3.9 KiB
Go
169 lines
3.9 KiB
Go
//go:generate go run generator.go
|
|
|
|
package jsonschema
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/jesseduffield/lazycore/pkg/utils"
|
|
"github.com/jesseduffield/lazygit/pkg/config"
|
|
"github.com/karimkhaleel/jsonschema"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
func GetSchemaDir() string {
|
|
return utils.GetLazyRootDirectory() + "/schema"
|
|
}
|
|
|
|
func GenerateSchema() *jsonschema.Schema {
|
|
schema := customReflect(&config.UserConfig{})
|
|
obj, _ := json.MarshalIndent(schema, "", " ")
|
|
obj = append(obj, '\n')
|
|
|
|
if err := os.WriteFile(GetSchemaDir()+"/config.json", obj, 0o644); err != nil {
|
|
fmt.Println("Error writing to file:", err)
|
|
return nil
|
|
}
|
|
return schema
|
|
}
|
|
|
|
func getSubSchema(rootSchema, parentSchema *jsonschema.Schema, key string) *jsonschema.Schema {
|
|
subSchema, found := parentSchema.Properties.Get(key)
|
|
if !found {
|
|
panic(fmt.Sprintf("Failed to find subSchema at %s on parent", key))
|
|
}
|
|
|
|
// This means the schema is defined on the rootSchema's Definitions
|
|
if subSchema.Ref != "" {
|
|
key, _ = strings.CutPrefix(subSchema.Ref, "#/$defs/")
|
|
refSchema, ok := rootSchema.Definitions[key]
|
|
if !ok {
|
|
panic(fmt.Sprintf("Failed to find #/$defs/%s", key))
|
|
}
|
|
refSchema.Description = subSchema.Description
|
|
return refSchema
|
|
}
|
|
|
|
return subSchema
|
|
}
|
|
|
|
func customReflect(v *config.UserConfig) *jsonschema.Schema {
|
|
r := &jsonschema.Reflector{FieldNameTag: "yaml", RequiredFromJSONSchemaTags: true}
|
|
if err := r.AddGoComments("github.com/jesseduffield/lazygit/pkg/config", "../config"); err != nil {
|
|
panic(err)
|
|
}
|
|
filterOutDevComments(r)
|
|
schema := r.Reflect(v)
|
|
defaultConfig := config.GetDefaultConfig()
|
|
userConfigSchema := schema.Definitions["UserConfig"]
|
|
|
|
defaultValue := reflect.ValueOf(defaultConfig).Elem()
|
|
|
|
yamlToFieldNames := lo.Invert(userConfigSchema.OriginalPropertiesMapping)
|
|
|
|
for pair := userConfigSchema.Properties.Oldest(); pair != nil; pair = pair.Next() {
|
|
yamlName := pair.Key
|
|
fieldName := yamlToFieldNames[yamlName]
|
|
|
|
subSchema := getSubSchema(schema, userConfigSchema, yamlName)
|
|
|
|
setDefaultVals(schema, subSchema, defaultValue.FieldByName(fieldName).Interface())
|
|
}
|
|
|
|
return schema
|
|
}
|
|
|
|
func filterOutDevComments(r *jsonschema.Reflector) {
|
|
for k, v := range r.CommentMap {
|
|
commentLines := strings.Split(v, "\n")
|
|
filteredCommentLines := lo.Filter(commentLines, func(line string, _ int) bool {
|
|
return !strings.Contains(line, "[dev]")
|
|
})
|
|
r.CommentMap[k] = strings.Join(filteredCommentLines, "\n")
|
|
}
|
|
}
|
|
|
|
func setDefaultVals(rootSchema, schema *jsonschema.Schema, defaults any) {
|
|
t := reflect.TypeOf(defaults)
|
|
v := reflect.ValueOf(defaults)
|
|
|
|
if t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
|
|
t = t.Elem()
|
|
v = v.Elem()
|
|
}
|
|
|
|
k := t.Kind()
|
|
_ = k
|
|
|
|
switch t.Kind() {
|
|
case reflect.Bool:
|
|
schema.Default = v.Bool()
|
|
case reflect.Int:
|
|
schema.Default = v.Int()
|
|
case reflect.String:
|
|
schema.Default = v.String()
|
|
default:
|
|
// Do nothing
|
|
}
|
|
|
|
if t.Kind() != reflect.Struct {
|
|
return
|
|
}
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
value := v.Field(i).Interface()
|
|
parentKey := t.Field(i).Name
|
|
|
|
key, ok := schema.OriginalPropertiesMapping[parentKey]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
subSchema := getSubSchema(rootSchema, schema, key)
|
|
|
|
if isStruct(value) {
|
|
setDefaultVals(rootSchema, subSchema, value)
|
|
} else if !isZeroValue(value) {
|
|
subSchema.Default = value
|
|
}
|
|
}
|
|
}
|
|
|
|
func isZeroValue(v any) bool {
|
|
switch v := v.(type) {
|
|
case int, int32, int64, float32, float64:
|
|
return v == 0
|
|
case string:
|
|
return v == ""
|
|
case bool:
|
|
return false
|
|
case nil:
|
|
return true
|
|
}
|
|
|
|
rv := reflect.ValueOf(v)
|
|
switch rv.Kind() {
|
|
case reflect.Slice, reflect.Map:
|
|
return rv.Len() == 0
|
|
case reflect.Ptr, reflect.Interface:
|
|
return rv.IsNil()
|
|
case reflect.Struct:
|
|
for i := 0; i < rv.NumField(); i++ {
|
|
if !isZeroValue(rv.Field(i).Interface()) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func isStruct(v any) bool {
|
|
return reflect.TypeOf(v).Kind() == reflect.Struct
|
|
}
|