diff --git a/docs/Config.md b/docs/Config.md index cf78a3113..c6f5cbbf9 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -90,6 +90,7 @@ gui: spinner: frames: ['|', '/', '-', '\\'] rate: 50 # spinner rate in milliseconds + statusPanelView: 'dashboard' # one of 'dashboard' | 'allBranchesLog' git: paging: colorArg: always diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index cd0d3e316..97f32688e 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -164,6 +164,10 @@ func loadUserConfig(configFiles []string, base *UserConfig) (*UserConfig, error) if err := yaml.Unmarshal(content, base); err != nil { return nil, fmt.Errorf("The config at `%s` couldn't be parsed, please inspect it before opening up an issue.\n%w", path, err) } + + if err := base.Validate(); err != nil { + return nil, fmt.Errorf("The config at `%s` has a validation error.\n%w", path, err) + } } return base, nil diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 20a404b16..29b46e903 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -147,6 +147,9 @@ type GuiConfig struct { FilterMode string `yaml:"filterMode" jsonschema:"enum=substring,enum=fuzzy"` // Config relating to the spinner. Spinner SpinnerConfig `yaml:"spinner"` + // Status panel view. + // One of 'dashboard' (default) | 'allBranchesLog' + StatusPanelView string `yaml:"statusPanelView" jsonschema:"enum=dashboard,enum=allBranchesLog"` } func (c *GuiConfig) UseFuzzySearch() bool { @@ -684,6 +687,7 @@ func GetDefaultConfig() *UserConfig { Frames: []string{"|", "/", "-", "\\"}, Rate: 50, }, + StatusPanelView: "dashboard", }, Git: GitConfig{ Paging: PagingConfig{ diff --git a/pkg/config/user_config_validation.go b/pkg/config/user_config_validation.go new file mode 100644 index 000000000..945979db9 --- /dev/null +++ b/pkg/config/user_config_validation.go @@ -0,0 +1,22 @@ +package config + +import ( + "fmt" + "slices" + "strings" +) + +func (config *UserConfig) Validate() error { + if err := validateEnum("gui.statusPanelView", config.Gui.StatusPanelView, []string{"dashboard", "allBranchesLog"}); err != nil { + return err + } + return nil +} + +func validateEnum(name string, value string, allowedValues []string) error { + if slices.Contains(allowedValues, value) { + return nil + } + allowedValuesStr := strings.Join(allowedValues, ", ") + return fmt.Errorf("Unexpected value '%s' for '%s'. Allowed values: %s", value, name, allowedValuesStr) +} diff --git a/pkg/config/user_config_validation_test.go b/pkg/config/user_config_validation_test.go new file mode 100644 index 000000000..9f7b4d74c --- /dev/null +++ b/pkg/config/user_config_validation_test.go @@ -0,0 +1,49 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestUserConfigValidate_enums(t *testing.T) { + type testCase struct { + value string + valid bool + } + + scenarios := []struct { + name string + setup func(config *UserConfig, value string) + testCases []testCase + }{ + { + name: "Gui.StatusPanelView", + setup: func(config *UserConfig, value string) { + config.Gui.StatusPanelView = value + }, + testCases: []testCase{ + {value: "dashboard", valid: true}, + {value: "allBranchesLog", valid: true}, + {value: "", valid: false}, + {value: "invalid_value", valid: false}, + }, + }, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + for _, testCase := range s.testCases { + config := GetDefaultConfig() + s.setup(config, testCase.value) + err := config.Validate() + + if testCase.valid { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + } + }) + } +} diff --git a/pkg/gui/controllers/status_controller.go b/pkg/gui/controllers/status_controller.go index 54bd04ad5..4fcb26a7c 100644 --- a/pkg/gui/controllers/status_controller.go +++ b/pkg/gui/controllers/status_controller.go @@ -83,35 +83,15 @@ func (self *StatusController) onClickMain(opts gocui.ViewMouseBindingOpts) error } func (self *StatusController) GetOnRenderToMain() func() error { - versionStr := "master" - version, err := types.ParseVersionNumber(self.c.GetConfig().GetVersion()) - if err == nil { - // Don't just take the version string as is, but format it again. This - // way it will be correct even if a distribution omits the "v", or the - // ".0" at the end. - versionStr = fmt.Sprintf("v%d.%d.%d", version.Major, version.Minor, version.Patch) - } + config := self.c.UserConfig.Gui - return func() error { - dashboardString := strings.Join( - []string{ - lazygitTitle(), - "Copyright 2022 Jesse Duffield", - fmt.Sprintf("Keybindings: %s", style.AttrUnderline.Sprint(fmt.Sprintf(constants.Links.Docs.Keybindings, versionStr))), - fmt.Sprintf("Config Options: %s", style.AttrUnderline.Sprint(fmt.Sprintf(constants.Links.Docs.Config, versionStr))), - fmt.Sprintf("Tutorial: %s", style.AttrUnderline.Sprint(constants.Links.Docs.Tutorial)), - fmt.Sprintf("Raise an Issue: %s", style.AttrUnderline.Sprint(constants.Links.Issues)), - fmt.Sprintf("Release Notes: %s", style.AttrUnderline.Sprint(constants.Links.Releases)), - style.FgMagenta.Sprintf("Become a sponsor: %s", style.AttrUnderline.Sprint(constants.Links.Donate)), // caffeine ain't free - }, "\n\n") + "\n" - - return self.c.RenderToMainViews(types.RefreshMainOpts{ - Pair: self.c.MainViewPairs().Normal, - Main: &types.ViewUpdateOpts{ - Title: self.c.Tr.StatusTitle, - Task: types.NewRenderStringTask(dashboardString), - }, - }) + switch config.StatusPanelView { + case "dashboard": + return self.showDashboard + case "allBranchesLog": + return self.showAllBranchLogs + default: + return self.showDashboard } } @@ -224,6 +204,37 @@ func (self *StatusController) showAllBranchLogs() error { }) } +func (self *StatusController) showDashboard() error { + versionStr := "master" + version, err := types.ParseVersionNumber(self.c.GetConfig().GetVersion()) + if err == nil { + // Don't just take the version string as is, but format it again. This + // way it will be correct even if a distribution omits the "v", or the + // ".0" at the end. + versionStr = fmt.Sprintf("v%d.%d.%d", version.Major, version.Minor, version.Patch) + } + + dashboardString := strings.Join( + []string{ + lazygitTitle(), + fmt.Sprintf("Copyright %d Jesse Duffield", time.Now().Year()), + fmt.Sprintf("Keybindings: %s", style.AttrUnderline.Sprint(fmt.Sprintf(constants.Links.Docs.Keybindings, versionStr))), + fmt.Sprintf("Config Options: %s", style.AttrUnderline.Sprint(fmt.Sprintf(constants.Links.Docs.Config, versionStr))), + fmt.Sprintf("Tutorial: %s", style.AttrUnderline.Sprint(constants.Links.Docs.Tutorial)), + fmt.Sprintf("Raise an Issue: %s", style.AttrUnderline.Sprint(constants.Links.Issues)), + fmt.Sprintf("Release Notes: %s", style.AttrUnderline.Sprint(constants.Links.Releases)), + style.FgMagenta.Sprintf("Become a sponsor: %s", style.AttrUnderline.Sprint(constants.Links.Donate)), // caffeine ain't free + }, "\n\n") + "\n" + + return self.c.RenderToMainViews(types.RefreshMainOpts{ + Pair: self.c.MainViewPairs().Normal, + Main: &types.ViewUpdateOpts{ + Title: self.c.Tr.StatusTitle, + Task: types.NewRenderStringTask(dashboardString), + }, + }) +} + func (self *StatusController) handleCheckForUpdate() error { return self.c.Helpers().Update.CheckForUpdateInForeground() } diff --git a/schema/config.json b/schema/config.json index 21860c39c..5d259cbd4 100644 --- a/schema/config.json +++ b/schema/config.json @@ -392,6 +392,15 @@ "additionalProperties": false, "type": "object", "description": "Config relating to the spinner." + }, + "statusPanelView": { + "type": "string", + "enum": [ + "dashboard", + "allBranchesLog" + ], + "description": "Status panel view.\nOne of 'dashboard' (default) | 'allBranchesLog'", + "default": "dashboard" } }, "additionalProperties": false,