Skip to content

Migrate null keybindings to <disabled>, and remove our yaml fork #3459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ require (
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/jesseduffield/yaml v2.1.0+incompatible
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
github.com/karimkhaleel/jsonschema v0.0.0-20231001195015-d933f0d94ea3
github.com/kyokomi/emoji/v2 v2.2.8
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,6 @@ github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOj
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5/go.mod h1:qxN4mHOAyeIDLP7IK7defgPClM/z1Kze8VVQiaEjzsQ=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
github.com/jesseduffield/yaml v2.1.0+incompatible/go.mod h1:w0xGhOSIJCGYYW+hnFPTutCy5aACpkcwbmORt5axGqk=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
Expand Down
18 changes: 17 additions & 1 deletion pkg/config/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/adrg/xdg"
"github.com/jesseduffield/lazygit/pkg/utils/yaml_utils"
yaml "github.com/jesseduffield/yaml"
"gopkg.in/yaml.v3"
)

// AppConfig contains the base configuration fields required for lazygit.
Expand Down Expand Up @@ -180,6 +180,11 @@ func migrateUserConfig(path string, content []byte) ([]byte, error) {
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
}

changedContent, err = changeNullKeybindingsToDisabled(changedContent)
if err != nil {
return nil, fmt.Errorf("Couldn't migrate config file at `%s`: %s", path, err)
}

// Add more migrations here...

// Write config back if changed
Expand All @@ -193,6 +198,17 @@ func migrateUserConfig(path string, content []byte) ([]byte, error) {
return content, nil
}

func changeNullKeybindingsToDisabled(changedContent []byte) ([]byte, error) {
return yaml_utils.Walk(changedContent, func(node *yaml.Node, path string) bool {
if strings.HasPrefix(path, "keybinding.") && node.Kind == yaml.ScalarNode && node.Tag == "!!null" {
node.Value = "<disabled>"
node.Tag = "!!str"
return true
}
return false
})
}

func (c *AppConfig) GetDebug() bool {
return c.Debug
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/config/dummies.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package config

import (
yaml "github.com/jesseduffield/yaml"
"gopkg.in/yaml.v3"
)

// NewDummyAppConfig creates a new dummy AppConfig for testing
Expand Down
71 changes: 71 additions & 0 deletions pkg/utils/yaml_utils/yaml_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,74 @@ func renameYamlKey(node *yaml.Node, path []string, newKey string) (bool, error)

return renameYamlKey(valueNode, path[1:], newKey)
}

// Traverses a yaml document, calling the callback function for each node. The
// callback is allowed to modify the node in place, in which case it should
// return true. The function returns the original yaml document if none of the
// callbacks returned true, and the modified document otherwise.
func Walk(yamlBytes []byte, callback func(node *yaml.Node, path string) bool) ([]byte, error) {
// Parse the YAML file.
var node yaml.Node
err := yaml.Unmarshal(yamlBytes, &node)
if err != nil {
return nil, fmt.Errorf("failed to parse YAML: %w", err)
}

// Empty document: nothing to do.
if len(node.Content) == 0 {
return yamlBytes, nil
}

body := node.Content[0]

if didChange, err := walk(body, "", callback); err != nil || !didChange {
return yamlBytes, err
}

// Convert the updated YAML node back to YAML bytes.
updatedYAMLBytes, err := yaml.Marshal(body)
if err != nil {
return nil, fmt.Errorf("failed to convert YAML node to bytes: %w", err)
}

return updatedYAMLBytes, nil
}

func walk(node *yaml.Node, path string, callback func(*yaml.Node, string) bool) (bool, error) {
didChange := callback(node, path)
switch node.Kind {
case yaml.DocumentNode:
return false, fmt.Errorf("Unexpected document node in the middle of a yaml tree")
case yaml.MappingNode:
for i := 0; i < len(node.Content); i += 2 {
name := node.Content[i].Value
childNode := node.Content[i+1]
var childPath string
if path == "" {
childPath = name
} else {
childPath = fmt.Sprintf("%s.%s", path, name)
}
didChangeChild, err := walk(childNode, childPath, callback)
if err != nil {
return false, err
}
didChange = didChange || didChangeChild
}
case yaml.SequenceNode:
for i := 0; i < len(node.Content); i++ {
childPath := fmt.Sprintf("%s[%d]", path, i)
didChangeChild, err := walk(node.Content[i], childPath, callback)
if err != nil {
return false, err
}
didChange = didChange || didChangeChild
}
case yaml.ScalarNode:
// nothing to do
case yaml.AliasNode:
return false, fmt.Errorf("Alias nodes are not supported")
}

return didChange, nil
}
116 changes: 116 additions & 0 deletions pkg/utils/yaml_utils/yaml_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)

func TestUpdateYamlValue(t *testing.T) {
Expand Down Expand Up @@ -199,3 +200,118 @@ func TestRenameYamlKey(t *testing.T) {
})
}
}

func TestWalk_paths(t *testing.T) {
tests := []struct {
name string
document string
expectedPaths []string
}{
{
name: "empty document",
document: "",
expectedPaths: []string{},
},
{
name: "scalar",
document: "x: 5",
expectedPaths: []string{"", "x"}, // called with an empty path for the root node
},
{
name: "nested",
document: "foo:\n x: 5",
expectedPaths: []string{"", "foo", "foo.x"},
},
{
name: "deeply nested",
document: "foo:\n bar:\n baz: 5",
expectedPaths: []string{"", "foo", "foo.bar", "foo.bar.baz"},
},
{
name: "array",
document: "foo:\n bar: [3, 7]",
expectedPaths: []string{"", "foo", "foo.bar", "foo.bar[0]", "foo.bar[1]"},
},
{
name: "nested arrays",
document: "foo:\n bar: [[3, 7], [8, 9]]",
expectedPaths: []string{"", "foo", "foo.bar", "foo.bar[0]", "foo.bar[0][0]", "foo.bar[0][1]", "foo.bar[1]", "foo.bar[1][0]", "foo.bar[1][1]"},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
paths := []string{}
_, err := Walk([]byte(test.document), func(node *yaml.Node, path string) bool {
paths = append(paths, path)
return true
})

assert.NoError(t, err)
assert.Equal(t, test.expectedPaths, paths)
})
}
}

func TestWalk_inPlaceChanges(t *testing.T) {
tests := []struct {
name string
in string
callback func(node *yaml.Node, path string) bool
expectedOut string
}{
{
name: "no change",
in: "x: 5",
callback: func(node *yaml.Node, path string) bool { return false },
expectedOut: "x: 5",
},
{
name: "change value",
in: "x: 5\ny: 3",
callback: func(node *yaml.Node, path string) bool {
if path == "x" {
node.Value = "7"
return true
}
return false
},
expectedOut: "x: 7\ny: 3\n",
},
{
name: "change nested value",
in: "x:\n y: 5",
callback: func(node *yaml.Node, path string) bool {
if path == "x.y" {
node.Value = "7"
return true
}
return false
},
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
expectedOut: "x:\n y: 7\n",
},
{
name: "change array value",
in: "x:\n - y: 5",
callback: func(node *yaml.Node, path string) bool {
if path == "x[0].y" {
node.Value = "7"
return true
}
return false
},
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
expectedOut: "x:\n - y: 7\n",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, err := Walk([]byte(test.in), test.callback)

assert.NoError(t, err)
assert.Equal(t, test.expectedOut, string(result))
})
}
}
Loading