diff options
Diffstat (limited to 'pkg/config')
-rw-r--r-- | pkg/config/app_config.go | 222 | ||||
-rw-r--r-- | pkg/config/config_default_platform.go | 10 | ||||
-rw-r--r-- | pkg/config/config_linux.go | 10 | ||||
-rw-r--r-- | pkg/config/config_windows.go | 10 | ||||
-rw-r--r-- | pkg/config/dummies.go | 10 | ||||
-rw-r--r-- | pkg/config/os_config.go | 10 | ||||
-rw-r--r-- | pkg/config/user_config.go | 215 |
7 files changed, 365 insertions, 122 deletions
diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 6558f9d3b..db0d56558 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -1,28 +1,27 @@ package config import ( - "bytes" "io/ioutil" "os" "path/filepath" - "github.com/shibukawa/configdir" - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" + "github.com/OpenPeeDeeP/xdg" + yaml "github.com/jesseduffield/yaml" ) // AppConfig contains the base configuration fields required for lazygit. type AppConfig struct { - Debug bool `long:"debug" env:"DEBUG" default:"false"` - Version string `long:"version" env:"VERSION" default:"unversioned"` - Commit string `long:"commit" env:"COMMIT"` - BuildDate string `long:"build-date" env:"BUILD_DATE"` - Name string `long:"name" env:"NAME" default:"lazygit"` - BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""` - UserConfig *viper.Viper - UserConfigDir string - AppState *AppState - IsNewRepo bool + Debug bool `long:"debug" env:"DEBUG" default:"false"` + Version string `long:"version" env:"VERSION" default:"unversioned"` + Commit string `long:"commit" env:"COMMIT"` + BuildDate string `long:"build-date" env:"BUILD_DATE"` + Name string `long:"name" env:"NAME" default:"lazygit"` + BuildSource string `long:"build-source" env:"BUILD_SOURCE" default:""` + UserConfig *UserConfig + UserConfigDir string + UserConfigPath string + AppState *AppState + IsNewRepo bool } // AppConfigurer interface allows individual app config structs to inherit Fields @@ -34,10 +33,11 @@ type AppConfigurer interface { GetBuildDate() string GetName() string GetBuildSource() string - GetUserConfig() *viper.Viper + GetUserConfig() *UserConfig GetUserConfigDir() string + GetUserConfigPath() string GetAppState() *AppState - WriteToUserConfig(string, interface{}) error + WriteToUserConfig(func(*UserConfig) error) error SaveAppState() error LoadAppState() error SetIsNewRepo(bool) @@ -46,7 +46,12 @@ type AppConfigurer interface { // NewAppConfig makes a new app config func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool) (*AppConfig, error) { - userConfig, userConfigPath, err := LoadConfig("config", true) + configDir, err := findOrCreateConfigDir(name) + if err != nil { + return nil, err + } + + userConfig, err := loadUserConfigWithDefaults(configDir) if err != nil { return nil, err } @@ -56,16 +61,17 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg } appConfig := &AppConfig{ - Name: "lazygit", - Version: version, - Commit: commit, - BuildDate: date, - Debug: debuggingFlag, - BuildSource: buildSource, - UserConfig: userConfig, - UserConfigDir: filepath.Dir(userConfigPath), - AppState: &AppState{}, - IsNewRepo: false, + Name: "lazygit", + Version: version, + Commit: commit, + BuildDate: date, + Debug: debuggingFlag, + BuildSource: buildSource, + UserConfig: userConfig, + UserConfigDir: configDir, + UserConfigPath: filepath.Join(configDir, "config.yml"), + AppState: &AppState{}, + IsNewRepo: false, } if err := appConfig.LoadAppState(); err != nil { @@ -75,6 +81,51 @@ func NewAppConfig(name, version, commit, date string, buildSource string, debugg return appConfig, nil } +func findOrCreateConfigDir(projectName string) (string, error) { + // chucking my name there is not for vanity purposes, the xdg spec (and that + // function) requires a vendor name. May as well line up with github + configDirs := xdg.New("jesseduffield", projectName) + folder := configDirs.ConfigHome() + + err := os.MkdirAll(folder, 0755) + if err != nil { + return "", err + } + + return folder, nil +} + +func loadUserConfigWithDefaults(configDir string) (*UserConfig, error) { + return loadUserConfig(configDir, GetDefaultConfig()) +} + +func loadUserConfig(configDir string, base *UserConfig) (*UserConfig, error) { + fileName := filepath.Join(configDir, "config.yml") + + if _, err := os.Stat(fileName); err != nil { + if os.IsNotExist(err) { + file, err := os.Create(fileName) + if err != nil { + return nil, err + } + file.Close() + } else { + return nil, err + } + } + + content, err := ioutil.ReadFile(fileName) + if err != nil { + return nil, err + } + + if err := yaml.Unmarshal(content, base); err != nil { + return nil, err + } + + return base, nil +} + // GetIsNewRepo returns known repo boolean func (c *AppConfig) GetIsNewRepo() bool { return c.IsNewRepo @@ -117,10 +168,15 @@ func (c *AppConfig) GetBuildSource() string { } // GetUserConfig returns the user config -func (c *AppConfig) GetUserConfig() *viper.Viper { +func (c *AppConfig) GetUserConfig() *UserConfig { return c.UserConfig } +// GetUserConfig returns the user config +func (c *AppConfig) GetUserConfigPath() string { + return c.UserConfigPath +} + // GetAppState returns the app state func (c *AppConfig) GetAppState() *AppState { return c.AppState @@ -130,78 +186,40 @@ func (c *AppConfig) GetUserConfigDir() string { return c.UserConfigDir } -func newViper(filename string) (*viper.Viper, error) { - v := viper.New() - v.SetConfigType("yaml") - v.SetConfigName(filename) - return v, nil -} - -// LoadConfig gets the user's config -func LoadConfig(filename string, withDefaults bool) (*viper.Viper, string, error) { - v, err := newViper(filename) - if err != nil { - return nil, "", err - } - if withDefaults { - if err = LoadDefaults(v, GetDefaultConfig()); err != nil { - return nil, "", err - } - if err = LoadDefaults(v, GetPlatformDefaultConfig()); err != nil { - return nil, "", err - } - } - configPath, err := LoadAndMergeFile(v, filename+".yml") +func configFilePath(filename string) (string, error) { + folder, err := findOrCreateConfigDir("lazygit") if err != nil { - return nil, "", err + return "", err } - return v, configPath, nil -} - -// LoadDefaults loads in the defaults defined in this file -func LoadDefaults(v *viper.Viper, defaults []byte) error { - return v.MergeConfig(bytes.NewBuffer(defaults)) -} -func prepareConfigFile(filename string) (string, error) { - // chucking my name there is not for vanity purposes, the xdg spec (and that - // function) requires a vendor name. May as well line up with github - configDirs := configdir.New("jesseduffield", "lazygit") - folder := configDirs.QueryFolderContainsFile(filename) - if folder == nil { - // create the file as empty - folders := configDirs.QueryFolders(configdir.Global) - if err := folders[0].WriteFile(filename, []byte{}); err != nil { - return "", err - } - folder = configDirs.QueryFolderContainsFile(filename) - } - return filepath.Join(folder.Path, filename), nil + return filepath.Join(folder, filename), nil } -// LoadAndMergeFile Loads the config/state file, creating -// the file has an empty one if it does not exist -func LoadAndMergeFile(v *viper.Viper, filename string) (string, error) { - configPath, err := prepareConfigFile(filename) +// WriteToUserConfig allows you to set a value on the user config to be saved +// note that if you set a zero-value, it may be ignored e.g. a false or 0 or +// empty string this is because we are using the omitempty yaml directive so +// that we don't write a heap of zero values to the user's config.yml +func (c *AppConfig) WriteToUserConfig(updateConfig func(*UserConfig) error) error { + userConfig, err := loadUserConfig(c.UserConfigDir, &UserConfig{}) if err != nil { - return "", err + return err } - v.AddConfigPath(filepath.Dir(configPath)) - return configPath, v.MergeInConfig() -} + if err := updateConfig(userConfig); err != nil { + return err + } -// WriteToUserConfig adds a key/value pair to the user's config and saves it -func (c *AppConfig) WriteToUserConfig(key string, value interface{}) error { - // reloading the user config directly (without defaults) so that we're not - // writing any defaults back to the user's config - v, _, err := LoadConfig("config", false) + file, err := os.OpenFile(c.ConfigFilename(), os.O_WRONLY|os.O_CREATE, 0666) if err != nil { return err } - v.Set(key, value) - return v.WriteConfig() + return yaml.NewEncoder(file).Encode(userConfig) +} + +// ConfigFilename returns the filename of the current config file +func (c *AppConfig) ConfigFilename() string { + return filepath.Join(c.UserConfigDir, "config.yml") } // SaveAppState marshalls the AppState struct and writes it to the disk @@ -211,7 +229,7 @@ func (c *AppConfig) SaveAppState() error { return err } - filepath, err := prepareConfigFile("state.yml") + filepath, err := configFilePath("state.yml") if err != nil { return err } @@ -221,7 +239,7 @@ func (c *AppConfig) SaveAppState() error { // LoadAppState loads recorded AppState from file func (c *AppConfig) LoadAppState() error { - filepath, err := prepareConfigFile("state.yml") + filepath, err := configFilePath("state.yml") if err != nil { return err } @@ -235,8 +253,20 @@ func (c *AppConfig) LoadAppState() error { return yaml.Unmarshal(appStateBytes, c.AppState) } -// GetDefaultConfig returns the application default configuration -func GetDefaultConfig() []byte { +func GetDefaultConfig() *UserConfig { + userConfig := &UserConfig{} + + if err := yaml.Unmarshal(GetDefaultConfigBytes(), userConfig); err != nil { + panic(err) + } + + userConfig.OS = GetPlatformDefaultConfig() + + return userConfig +} + +// GetDefaultConfigBytes returns the application default configuration +func GetDefaultConfigBytes() []byte { return []byte( `gui: ## stuff relating to the UI @@ -414,12 +444,6 @@ func getDefaultAppState() []byte { `) } -func globalConfigDir() string { - configDirs := configdir.New("jesseduffield", "lazygit") - configDir := configDirs.QueryFolders(configdir.Global)[0] - return configDir.Path -} - -func LogPath() string { - return filepath.Join(globalConfigDir(), "development.log") +func LogPath() (string, error) { + return configFilePath("development.log") } diff --git a/pkg/config/config_default_platform.go b/pkg/config/config_default_platform.go index df205c0d7..e9e2f63ba 100644 --- a/pkg/config/config_default_platform.go +++ b/pkg/config/config_default_platform.go @@ -3,9 +3,9 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform -func GetPlatformDefaultConfig() []byte { - return []byte( - `os: - openCommand: 'open {{filename}}' - openLinkCommand: 'open {{link}}'`) +func GetPlatformDefaultConfig() OSConfig { + return OSConfig{ + OpenCommand: "open {{filename}}", + OpenLinkCommand: "open {{link}}", + } } diff --git a/pkg/config/config_linux.go b/pkg/config/config_linux.go index 2dfbdb1c6..b3d7f42b8 100644 --- a/pkg/config/config_linux.go +++ b/pkg/config/config_linux.go @@ -1,9 +1,9 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform -func GetPlatformDefaultConfig() []byte { - return []byte( - `os: - openCommand: 'sh -c "xdg-open {{filename}} >/dev/null"' - openLinkCommand: 'sh -c "xdg-open {{link}} >/dev/null"'`) +func GetPlatformDefaultConfig() OSConfig { + return OSConfig{ + OpenCommand: `sh -c "xdg-open {{filename}} >/dev/null"`, + OpenLinkCommand: `sh -c "xdg-open {{link}} >/dev/null"`, + } } diff --git a/pkg/config/config_windows.go b/pkg/config/config_windows.go index 6f6560316..cea722424 100644 --- a/pkg/config/config_windows.go +++ b/pkg/config/config_windows.go @@ -1,9 +1,9 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform -func GetPlatformDefaultConfig() []byte { - return []byte( - `os: - openCommand: 'cmd /c "start "" {{filename}}"' - openLinkCommand: 'cmd /c "start "" {{link}}"'`) +func GetPlatformDefaultConfig() OSConfig { + return OSConfig{ + OpenCommand: `cmd /c "start "" {{filename}}"`, + OpenLinkCommand: `cmd /c "start "" {{link}}"`, + } } diff --git a/pkg/config/dummies.go b/pkg/config/dummies.go index 034d9b88b..40548d732 100644 --- a/pkg/config/dummies.go +++ b/pkg/config/dummies.go @@ -1,17 +1,11 @@ package config import ( - "github.com/spf13/viper" - yaml "gopkg.in/yaml.v2" + yaml "github.com/jesseduffield/yaml" ) // NewDummyAppConfig creates a new dummy AppConfig for testing func NewDummyAppConfig() *AppConfig { - userConfig := viper.New() - userConfig.SetConfigType("yaml") - if err := LoadDefaults(userConfig, GetDefaultConfig()); err != nil { - panic(err) - } appConfig := &AppConfig{ Name: "lazygit", Version: "unversioned", @@ -19,7 +13,7 @@ func NewDummyAppConfig() *AppConfig { BuildDate: "", Debug: false, BuildSource: "", - UserConfig: userConfig, + UserConfig: GetDefaultConfig(), } _ = yaml.Unmarshal([]byte{}, appConfig.AppState) return appConfig diff --git a/pkg/config/os_config.go b/pkg/config/os_config.go new file mode 100644 index 000000000..b24e0ced3 --- /dev/null +++ b/pkg/config/os_config.go @@ -0,0 +1,10 @@ +package config + +// OSConfig contains config on the level of the os +type OSConfig struct { + // OpenCommand is the command for opening a file + OpenCommand string `yaml:"openCommand,omitempty"` + + // OpenCommand is the command for opening a link + OpenLinkCommand string `yaml:"openLinkCommand,omitempty"` +} diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go new file mode 100644 index 000000000..e058d5c5a --- /dev/null +++ b/pkg/config/user_config.go @@ -0,0 +1,215 @@ +package config + +type CommitPrefixConfig struct { + Pattern string `yaml:"pattern"` + Replace string `yaml:"replace"` +} + +type ThemeConfig struct { + LightTheme bool `yaml:"lightTheme"` + ActiveBorderColor []string `yaml:"activeBorderColor"` + InactiveBorderColor []string `yaml:"inactiveBorderColor"` + OptionsTextColor []string `yaml:"optionsTextColor"` + SelectedLineBgColor []string `yaml:"selectedLineBgColor"` + SelectedRangeBgColor []string `yaml:"selectedRangeBgColor"` +} + +type CustomCommandMenuOption struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Value string `yaml:"value"` +} + +type CustomCommandPrompt struct { + Type string `yaml:"type"` // one of 'input' and 'menu' + Title string `yaml:"title"` + + // this only apply to prompts + InitialValue string `yaml:"initialValue"` + + // this only applies to menus + Options []CustomCommandMenuOption +} + +type CustomCommand struct { + Key string `yaml:"key"` + Context string `yaml:"context"` + Command string `yaml:"command"` + Subprocess bool `yaml:"subprocess"` + Prompts []CustomCommandPrompt `yaml:"prompts"` + LoadingText string `yaml:"loadingText"` + Description string `yaml:"description"` +} + +type UserConfig struct { + Gui struct { + ScrollHeight int `yaml:"scrollHeight"` + ScrollPastBottom bool `yaml:"scrollPastBottom"` + MouseEvents bool `yaml:"mouseEvents"` + SkipUnstageLineWarning bool `yaml:"skipUnstageLineWarning"` + SkipStashWarning bool `yaml:"skipStashWarning"` + SidePanelWidth float64 `yaml:"sidePanelWidth"` + ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"` + MainPanelSplitMode string `yaml:"mainPanelSplitMode"` + Theme ThemeConfig `yaml:"theme"` + CommitLength struct { + Show bool `yaml:"show"` + } `yaml:"commitLength"` + } `yaml:"gui"` + Git struct { + Paging struct { + ColorArg string `yaml:"colorArg"` + Pager string `yaml:"pager"` + UseConfig bool `yaml:"useConfig"` + } `yaml:"paging"` + Merging struct { + ManualCommit bool `yaml:"manualCommit"` + Args string `yaml:"args"` + } `yaml:"merging"` + Pull struct { + Mode string `yaml:"mode"` + } `yaml:"pull"` + SkipHookPrefix string `yaml:"skipHookPrefix"` + AutoFetch bool `yaml:"autoFetch"` + BranchLogCmd string `yaml:"branchLogCmd"` + OverrideGpg bool `yaml:"overrideGpg"` + DisableForcePushing bool `yaml:"disableForcePushing"` + CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"` + } `yaml:"git"` + Update struct { + Method string `yaml:"method"` + Days int64 `yaml:"days"` + } `yaml:"update"` + Reporting string `yaml:"reporting"` + SplashUpdatesIndex int `yaml:"splashUpdatesIndex"` + ConfirmOnQuit bool `yaml:"confirmOnQuit"` + QuitOnTopLevelReturn bool `yaml:"quitOnTopLevelReturn"` + Keybinding struct { + Universal struct { + Quit string `yaml:"quit"` + QuitAlt1 string `yaml:"quit-alt1"` + Return string `yaml:"return"` + QuitWithoutChangingDirectory string `yaml:"quitWithoutChangingDirectory"` + TogglePanel string `yaml:"togglePanel"` + PrevItem string `yaml:"prevItem"` + NextItem string `yaml:"nextItem"` + PrevItemAlt string `yaml:"prevItem-alt"` + NextItemAlt string `yaml:"nextItem-alt"` + PrevPage string `yaml:"prevPage"` + NextPage string `yaml:"nextPage"` + GotoTop string `yaml:"gotoTop"` + GotoBottom string `yaml:"gotoBottom"` + PrevBlock string `yaml:"prevBlock"` + NextBlock string `yaml:"nextBlock"` + PrevBlockAlt string `yaml:"prevBlock-alt"` + NextBlockAlt string `yaml:"nextBlock-alt"` + NextMatch string `yaml:"nextMatch"` + PrevMatch string `yaml:"prevMatch"` + StartSearch string `yaml:"startSearch"` + OptionMenu string `yaml:"optionMenu"` + OptionMenuAlt1 string `yaml:"optionMenu-alt1"` + Select string `yaml:"select"` + GoInto string `yaml:"goInto"` + Confirm string `yaml:"confirm"` + ConfirmAlt1 string `yaml:"confirm-alt1"` + Remove string `yaml:"remove"` + New string `yaml:"new"` + Edit string `yaml:"edit"` + OpenFile string `yaml:"openFile"` + ScrollUpMain string `yaml:"scrollUpMain"` + ScrollDownMain string `yaml:"scrollDownMain"` + ScrollUpMainAlt1 string `yaml:"scrollUpMain-alt1"` + ScrollDownMainAlt1 string `yaml:"scrollDownMain-alt1"` + ScrollUpMainAlt2 string `yaml:"scrollUpMain-alt2"` + ScrollDownMainAlt2 string `yaml:"scrollDownMain-alt2"` + ExecuteCustomCommand string `yaml:"executeCustomCommand"` + CreateRebaseOptionsMenu string `yaml:"createRebaseOptionsMenu"` + PushFiles string `yaml:"pushFiles"` + PullFiles string `yaml:"pullFiles"` + Refresh string `yaml:"refresh"` + CreatePatchOptionsMenu string `yaml:"createPatchOptionsMenu"` + NextTab string `yaml:"nextTab"` + PrevTab string `yaml:"prevTab"` + NextScreenMode string `yaml:"nextScreenMode"` + PrevScreenMode string `yaml:"prevScreenMode"` + Undo string `yaml:"undo"` + Redo string `yaml:"redo"` + FilteringMenu string `yaml:"filteringMenu"` + DiffingMenu string `yaml:"diffingMenu"` + DiffingMenuAlt string `yaml:"diffingMenu-alt"` + CopyToClipboard string `yaml:"copyToClipboard"` + } `yaml:"universal"` + Status struct { + CheckForUpdate string `yaml:"checkForUpdate"` + RecentRepos string `yaml:"recentRepos"` + } `yaml:"status"` + Files struct { + CommitChanges string `yaml:"commitChanges"` + CommitChangesWithoutHook string `yaml:"commitChangesWithoutHook"` + AmendLastCommit string `yaml:"amendLastCommit"` + CommitChangesWithEditor string `yaml:"commitChangesWithEditor"` + IgnoreFile string `yaml:"ignoreFile"` + RefreshFiles string `yaml:"refreshFiles"` + StashAllChanges string `yaml:"stashAllChanges"` + ViewStashOptions string `yaml:"viewStashOptions"` + ToggleStagedAll string `yaml:"toggleStagedAll"` + ViewResetOptions string `yaml:"viewResetOptions"` + Fetch string `yaml:"fetch"` + } `yaml:"files"` + Branches struct { + CreatePullRequest string `yaml:"createPullRequest"` + CheckoutBranchByName string `yaml:"checkoutBranchByName"` + ForceCheckoutBranch string `yaml:"forceCheckoutBranch"` + RebaseBranch string `yaml:"rebaseBranch"` + RenameBranch string `yaml:"renameBranch"` + MergeIntoCurrentBranch string `yaml:"mergeIntoCurrentBranch"` + ViewGitFlowOptions string `yaml:"viewGitFlowOptions"` + FastForward string `yaml:"fastForward"` + PushTag string `yaml:"pushTag"` + SetUpstream string `yaml:"setUpstream"` + FetchRemote string `yaml:"fetchRemote"` + } `yaml:"branches"` + Commits struct { + SquashDown string `yaml:"squashDown"` + RenameCommit string `yaml:"renameCommit"` + RenameCommitWithEditor string `yaml:"renameCommitWithEditor"` + ViewResetOptions string `yaml:"viewResetOptions"` + MarkCommitAsFixup string `yaml:"markCommitAsFixup"` + CreateFixupCommit string `yaml:"createFixupCommit"` + SquashAboveCommits string `yaml:"squashAboveCommits"` + MoveDownCommit string `yaml:"moveDownCommit"` + MoveUpCommit string `yaml:"moveUpCommit"` + AmendToCommit string `yaml:"amendToCommit"` + PickCommit string `yaml:"pickCommit"` + RevertCommit string `yaml:"revertCommit"` + CherryPickCopy string `yaml:"cherryPickCopy"` + CherryPickCopyRange string `yaml:"cherryPickCopyRange"` + PasteCommits string `yaml:"pasteCommits"` + TagCommit string `yaml:"tagCommit"` + CheckoutCommit string `yaml:"checkoutCommit"` + ResetCherryPick string `yaml:"resetCherryPick"` + } `yaml:"commits"` + Stash struct { + PopStash string `yaml:"popStash"` + } `yaml:"stash"` + CommitFiles struct { + CheckoutCommitFile string `yaml:"checkoutCommitFile"` + } `yaml:"commitFiles"` + Main struct { + ToggleDragSelect string `yaml:"toggleDragSelect"` + ToggleDragSelectAlt string `yaml:"toggleDragSelect-alt"` + ToggleSelectHunk string `yaml:"toggleSelectHunk"` + PickBothHunks string `yaml:"pickBothHunks"` + } `yaml:"main"` + Submodules struct { + Init string `yaml:"init"` + Update string `yaml:"update"` + BulkMenu string `yaml:"bulkMenu"` + } `yaml:"submodules"` + } `yaml:"keybinding"` + // OS determines what defaults are set for opening files and links + OS OSConfig `yaml:"os,omitempty"` + StartupPopupVersion int `yaml:"startupPopupVersion"` + CustomCommands []CustomCommand `yaml:"customCommands"` + Services map[string]string `yaml:"services"` +} |