diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index ade614f7d..00ce83bc0 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "reflect" + "regexp" "runtime" "strings" "time" @@ -364,6 +365,11 @@ func computeMigratedConfig(path string, content []byte, changes *ChangesSet) ([] return nil, false, fmt.Errorf("Couldn't migrate config file at `%s`: %w", path, err) } + err = migrateBranchColors(&rootNode, changes) + if err != nil { + return nil, false, fmt.Errorf("Couldn't migrate config file at `%s`: %w", path, err) + } + // Add more migrations here... if reflect.DeepEqual(rootNode, originalCopy) { @@ -630,6 +636,43 @@ func migratePagersToDiffRenderers(rootNode *yaml.Node, changes *ChangesSet) erro }) } +// The deprecated gui.branchColors matched its keys against the part of a branch +// name before the first slash. Turn each key into a pattern that matches the +// same branches. If the file has a non-empty gui.branchColorPatterns, +// gui.branchColors was ignored, so remove it. +func migrateBranchColors(rootNode *yaml.Node, changes *ChangesSet) error { + return yaml_utils.TransformNode(rootNode, []string{"gui"}, func(guiNode *yaml.Node) error { + branchColorsKeyNode, branchColorsValueNode := yaml_utils.LookupKey(guiNode, "branchColors") + if branchColorsKeyNode == nil || branchColorsValueNode.Kind != yaml.MappingNode { + return nil + } + + patternsKeyNode, patternsValueNode := yaml_utils.LookupKey(guiNode, "branchColorPatterns") + if patternsKeyNode != nil { + switch { + case patternsValueNode.Kind == yaml.MappingNode && len(patternsValueNode.Content) > 0: + yaml_utils.RemoveKey(guiNode, "branchColors") + changes.Add("Removed 'gui.branchColors'; it had no effect because 'gui.branchColorPatterns' is set") + return nil + case patternsValueNode.Kind == yaml.MappingNode || patternsValueNode.Tag == "!!null": + yaml_utils.RemoveKey(guiNode, "branchColorPatterns") + default: + return nil + } + } + + branchColorsKeyNode.Value = "branchColorPatterns" + for i := 0; i < len(branchColorsValueNode.Content)-1; i += 2 { + keyNode := branchColorsValueNode.Content[i] + keyNode.Value = "^" + regexp.QuoteMeta(keyNode.Value) + "(/|$)" + keyNode.Tag = "!!str" + } + changes.Add("Converted 'gui.branchColors' to 'gui.branchColorPatterns'") + + return nil + }) +} + func hasNonNullScalarValue(node *yaml.Node) bool { return node != nil && node.Kind == yaml.ScalarNode && node.Tag != "!!null" && node.Value != "" } diff --git a/pkg/config/app_config_test.go b/pkg/config/app_config_test.go index 180f4b882..16e960828 100644 --- a/pkg/config/app_config_test.go +++ b/pkg/config/app_config_test.go @@ -821,3 +821,107 @@ func TestPagerMigration(t *testing.T) { }) } } + +func TestBranchColorsMigration(t *testing.T) { + scenarios := []struct { + name string + input string + expected string + expectedDidChange bool + expectedChanges []string + }{ + { + name: "No branchColors", + input: "gui:\n" + + " branchColorPatterns:\n" + + " '^docs/': blue\n", + expectedDidChange: false, + expectedChanges: []string{}, + }, + { + name: "branchColors is not an object", + input: "gui:\n" + + " branchColors: 5\n", + expectedDidChange: false, + expectedChanges: []string{}, + }, + { + name: "branchColors is converted to patterns in place", + input: "gui:\n" + + " scrollHeight: 2\n" + + " branchColors:\n" + + " feature: green\n" + + " v1.x: '#ff0000'\n" + + " 123: red\n" + + " mouseEvents: false\n", + expected: "gui:\n" + + " scrollHeight: 2\n" + + " branchColorPatterns:\n" + + " ^feature(/|$): green\n" + + " ^v1\\.x(/|$): '#ff0000'\n" + + " ^123(/|$): red\n" + + " mouseEvents: false\n", + expectedDidChange: true, + expectedChanges: []string{"Converted 'gui.branchColors' to 'gui.branchColorPatterns'"}, + }, + { + name: "branchColors is removed if branchColorPatterns is set", + input: "gui:\n" + + " branchColors:\n" + + " feature: green\n" + + " branchColorPatterns:\n" + + " '^docs/': blue\n", + expected: "gui:\n" + + " branchColorPatterns:\n" + + " '^docs/': blue\n", + expectedDidChange: true, + expectedChanges: []string{"Removed 'gui.branchColors'; it had no effect because 'gui.branchColorPatterns' is set"}, + }, + { + name: "branchColors replaces an empty branchColorPatterns", + input: "gui:\n" + + " branchColorPatterns: {}\n" + + " branchColors:\n" + + " feature: green\n", + expected: "gui:\n" + + " branchColorPatterns:\n" + + " ^feature(/|$): green\n", + expectedDidChange: true, + expectedChanges: []string{"Converted 'gui.branchColors' to 'gui.branchColorPatterns'"}, + }, + { + name: "branchColors replaces a null branchColorPatterns", + input: "gui:\n" + + " branchColorPatterns:\n" + + " branchColors:\n" + + " feature: green\n", + expected: "gui:\n" + + " branchColorPatterns:\n" + + " ^feature(/|$): green\n", + expectedDidChange: true, + expectedChanges: []string{"Converted 'gui.branchColors' to 'gui.branchColorPatterns'"}, + }, + { + name: "branchColors is kept if branchColorPatterns is not an object", + input: "gui:\n" + + " branchColorPatterns: 5\n" + + " branchColors:\n" + + " feature: green\n", + expectedDidChange: false, + expectedChanges: []string{}, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + changes := NewChangesSet() + actual, didChange, err := computeMigratedConfig("path doesn't matter", []byte(s.input), changes) + assert.NoError(t, err) + assert.Equal(t, s.expectedDidChange, didChange) + if didChange { + assert.Equal(t, s.expected, string(actual)) + } + assert.Equal(t, s.expectedChanges, changes.ToSliceFromOldest()) + }) + } +} diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index d05808159..b25488e87 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -71,9 +71,6 @@ type GuiConfig struct { // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-author-color AuthorColors map[string]string `yaml:"authorColors"` // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-branch-color - // Deprecated: use branchColorPatterns instead - BranchColors map[string]string `yaml:"branchColors" jsonschema:"deprecated"` - // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-branch-color BranchColorPatterns map[string]string `yaml:"branchColorPatterns"` // Custom icons for filenames and file extensions // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-files-icon--color diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 3d04e160d..a5dafcdbc 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -526,12 +526,7 @@ func (gui *Gui) onUserConfigLoaded() error { icons.SetNerdFontsVersion("") } - if len(userConfig.Gui.BranchColorPatterns) > 0 { - presentation.SetCustomBranches(userConfig.Gui.BranchColorPatterns, true) - } else { - // Fall back to the deprecated branchColors config - presentation.SetCustomBranches(userConfig.Gui.BranchColors, false) - } + presentation.SetCustomBranches(userConfig.Gui.BranchColorPatterns) return nil } diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go index f58f34dc6..18025ebd8 100644 --- a/pkg/gui/presentation/branches.go +++ b/pkg/gui/presentation/branches.go @@ -21,7 +21,6 @@ import ( type colorMatcher struct { patterns map[string]*style.TextStyle - isRegex bool // NOTE: this value is needed only until the deprecated branchColors config is removed and only regex color patterns are used } var colorPatterns *colorMatcher @@ -201,17 +200,9 @@ func GetBranchTextStyle(name string) style.TextStyle { } func (m *colorMatcher) match(name string) (*style.TextStyle, bool) { - if m.isRegex { - for pattern, style := range m.patterns { - if matched, _ := regexp.MatchString(pattern, name); matched { - return style, true - } - } - } else { - // old behavior using the deprecated branchColors behavior matching on branch type - branchType := strings.Split(name, "/")[0] - if value, ok := m.patterns[branchType]; ok { - return value, true + for pattern, style := range m.patterns { + if matched, _ := regexp.MatchString(pattern, name); matched { + return style, true } } @@ -271,10 +262,9 @@ func divergenceStr( return result } -func SetCustomBranches(customBranchColors map[string]string, isRegex bool) { +func SetCustomBranches(customBranchColors map[string]string) { colorPatterns = &colorMatcher{ patterns: utils.SetCustomColors(customBranchColors), - isRegex: isRegex, } } diff --git a/pkg/gui/presentation/branches_test.go b/pkg/gui/presentation/branches_test.go index 3d83ca0ba..60aca80f2 100644 --- a/pkg/gui/presentation/branches_test.go +++ b/pkg/gui/presentation/branches_test.go @@ -419,7 +419,7 @@ func Test_getBranchDisplayStrings(t *testing.T) { defer color.ForceSetColorLevel(oldColorLevel) c := common.NewDummyCommon() - SetCustomBranches(c.UserConfig().Gui.BranchColorPatterns, true) + SetCustomBranches(c.UserConfig().Gui.BranchColorPatterns) for i, s := range scenarios { icons.SetNerdFontsVersion(lo.Ternary(s.useIcons, "3", "")) diff --git a/schema-master/config.json b/schema-master/config.json index 9434f6016..f159995d8 100644 --- a/schema-master/config.json +++ b/schema-master/config.json @@ -537,13 +537,6 @@ "type": "object", "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-author-color" }, - "branchColors": { - "additionalProperties": { - "type": "string" - }, - "type": "object", - "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-branch-color\nDeprecated: use branchColorPatterns instead" - }, "branchColorPatterns": { "additionalProperties": { "type": "string"