1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-12 11:15:00 +02:00
lazygit/pkg/config/app_config.go

382 lines
9.2 KiB
Go
Raw Normal View History

package config
2018-08-15 13:34:25 +02:00
import (
"bytes"
2018-08-25 07:55:49 +02:00
"io/ioutil"
2019-02-18 12:29:43 +02:00
"os"
2018-08-25 07:55:49 +02:00
"path/filepath"
2018-08-15 13:34:25 +02:00
"github.com/shibukawa/configdir"
2018-08-15 13:34:25 +02:00
"github.com/spf13/viper"
2018-08-25 07:55:49 +02:00
yaml "gopkg.in/yaml.v2"
2018-08-15 13:34:25 +02:00
)
// AppConfig contains the base configuration fields required for lazygit.
type AppConfig struct {
2019-09-15 11:19:39 +02:00
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
}
// AppConfigurer interface allows individual app config structs to inherit Fields
// from AppConfig and still be used by lazygit.
type AppConfigurer interface {
GetDebug() bool
GetVersion() string
GetCommit() string
GetBuildDate() string
GetName() string
2018-08-25 07:55:49 +02:00
GetBuildSource() string
2018-08-15 13:34:25 +02:00
GetUserConfig() *viper.Viper
2019-09-15 11:19:39 +02:00
GetUserConfigDir() string
2018-08-25 07:55:49 +02:00
GetAppState() *AppState
2019-11-10 13:07:45 +02:00
WriteToUserConfig(string, interface{}) error
2018-08-25 07:55:49 +02:00
SaveAppState() error
LoadAppState() error
2018-12-10 14:45:03 +02:00
SetIsNewRepo(bool)
GetIsNewRepo() bool
2018-08-15 13:34:25 +02:00
}
// NewAppConfig makes a new app config
2019-02-18 12:29:43 +02:00
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag bool) (*AppConfig, error) {
userConfig, userConfigPath, err := LoadConfig("config", true)
2018-08-15 13:34:25 +02:00
if err != nil {
2018-08-25 07:55:49 +02:00
return nil, err
2018-08-15 13:34:25 +02:00
}
2019-02-18 12:29:43 +02:00
if os.Getenv("DEBUG") == "TRUE" {
debuggingFlag = true
}
2018-08-15 13:34:25 +02:00
appConfig := &AppConfig{
2019-09-15 11:19:39 +02:00
Name: "lazygit",
Version: version,
Commit: commit,
BuildDate: date,
Debug: debuggingFlag,
BuildSource: buildSource,
UserConfig: userConfig,
UserConfigDir: filepath.Dir(userConfigPath),
AppState: &AppState{},
IsNewRepo: false,
2018-08-25 07:55:49 +02:00
}
if err := appConfig.LoadAppState(); err != nil {
return nil, err
2018-08-15 13:34:25 +02:00
}
2018-08-25 07:55:49 +02:00
2018-08-15 13:34:25 +02:00
return appConfig, nil
}
2018-12-10 14:45:03 +02:00
// GetIsNewRepo returns known repo boolean
func (c *AppConfig) GetIsNewRepo() bool {
return c.IsNewRepo
}
// SetIsNewRepo set if the current repo is known
func (c *AppConfig) SetIsNewRepo(toSet bool) {
c.IsNewRepo = toSet
}
// GetDebug returns debug flag
func (c *AppConfig) GetDebug() bool {
return c.Debug
}
// GetVersion returns debug flag
func (c *AppConfig) GetVersion() string {
return c.Version
}
// GetCommit returns debug flag
func (c *AppConfig) GetCommit() string {
return c.Commit
}
// GetBuildDate returns debug flag
func (c *AppConfig) GetBuildDate() string {
return c.BuildDate
}
// GetName returns debug flag
func (c *AppConfig) GetName() string {
return c.Name
}
2018-08-15 13:34:25 +02:00
2018-08-25 07:55:49 +02:00
// GetBuildSource returns the source of the build. For builds from goreleaser
// this will be binaryBuild
func (c *AppConfig) GetBuildSource() string {
return c.BuildSource
}
2018-08-15 13:34:25 +02:00
// GetUserConfig returns the user config
func (c *AppConfig) GetUserConfig() *viper.Viper {
return c.UserConfig
}
2018-08-25 07:55:49 +02:00
// GetAppState returns the app state
func (c *AppConfig) GetAppState() *AppState {
return c.AppState
}
2019-09-15 11:19:39 +02:00
func (c *AppConfig) GetUserConfigDir() string {
return c.UserConfigDir
}
2018-08-25 07:55:49 +02:00
func newViper(filename string) (*viper.Viper, error) {
2018-08-15 13:34:25 +02:00
v := viper.New()
v.SetConfigType("yaml")
2018-08-25 07:55:49 +02:00
v.SetConfigName(filename)
2018-08-18 05:22:05 +02:00
return v, nil
}
2018-08-25 07:55:49 +02:00
// LoadConfig gets the user's config
func LoadConfig(filename string, withDefaults bool) (*viper.Viper, string, error) {
2018-08-25 07:55:49 +02:00
v, err := newViper(filename)
2018-08-15 13:34:25 +02:00
if err != nil {
return nil, "", err
2018-08-15 13:34:25 +02:00
}
2018-09-01 06:33:01 +02:00
if withDefaults {
if err = LoadDefaults(v, GetDefaultConfig()); err != nil {
return nil, "", err
2018-09-01 06:33:01 +02:00
}
if err = LoadDefaults(v, GetPlatformDefaultConfig()); err != nil {
return nil, "", err
2018-08-25 07:55:49 +02:00
}
}
configPath, err := LoadAndMergeFile(v, filename+".yml")
if err != nil {
return nil, "", err
2018-08-18 05:22:05 +02:00
}
return v, configPath, nil
2018-08-18 05:22:05 +02:00
}
2018-08-25 07:55:49 +02:00
// LoadDefaults loads in the defaults defined in this file
func LoadDefaults(v *viper.Viper, defaults []byte) error {
2018-09-01 06:33:01 +02:00
return v.MergeConfig(bytes.NewBuffer(defaults))
2018-08-18 05:22:05 +02:00
}
2018-08-25 07:55:49 +02:00
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")
2018-08-25 07:55:49 +02:00
folder := configDirs.QueryFolderContainsFile(filename)
2018-08-18 05:22:05 +02:00
if folder == nil {
2018-08-25 07:55:49 +02:00
// create the file as empty
2018-08-18 05:22:05 +02:00
folders := configDirs.QueryFolders(configdir.Global)
2018-08-25 07:55:49 +02:00
if err := folders[0].WriteFile(filename, []byte{}); err != nil {
return "", err
2018-08-18 05:54:39 +02:00
}
2018-08-25 07:55:49 +02:00
folder = configDirs.QueryFolderContainsFile(filename)
2018-08-15 13:34:25 +02:00
}
2018-08-25 07:55:49 +02:00
return filepath.Join(folder.Path, filename), nil
2018-08-18 05:22:05 +02:00
}
2018-08-25 07:55:49 +02:00
// LoadAndMergeFile Loads the config/state file, creating
2018-12-10 14:45:03 +02:00
// the file has an empty one if it does not exist
func LoadAndMergeFile(v *viper.Viper, filename string) (string, error) {
2018-08-25 07:55:49 +02:00
configPath, err := prepareConfigFile(filename)
2018-08-18 05:22:05 +02:00
if err != nil {
return "", err
2018-08-18 05:22:05 +02:00
}
2018-08-25 07:55:49 +02:00
v.AddConfigPath(filepath.Dir(configPath))
return configPath, v.MergeInConfig()
2018-08-25 07:55:49 +02:00
}
// WriteToUserConfig adds a key/value pair to the user's config and saves it
2019-11-10 13:07:45 +02:00
func (c *AppConfig) WriteToUserConfig(key string, value interface{}) error {
2018-08-25 07:55:49 +02:00
// 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)
2018-08-25 07:55:49 +02:00
if err != nil {
2018-08-18 05:22:05 +02:00
return err
}
2018-08-25 07:55:49 +02:00
2018-08-18 05:22:05 +02:00
v.Set(key, value)
2018-08-21 08:41:31 +02:00
return v.WriteConfig()
2018-08-15 13:34:25 +02:00
}
2019-05-22 15:34:29 +02:00
// SaveAppState marshalls the AppState struct and writes it to the disk
2018-08-25 07:55:49 +02:00
func (c *AppConfig) SaveAppState() error {
marshalledAppState, err := yaml.Marshal(c.AppState)
if err != nil {
return err
}
filepath, err := prepareConfigFile("state.yml")
if err != nil {
return err
}
return ioutil.WriteFile(filepath, marshalledAppState, 0644)
}
2018-08-29 13:32:34 +02:00
// LoadAppState loads recorded AppState from file
2018-08-25 07:55:49 +02:00
func (c *AppConfig) LoadAppState() error {
filepath, err := prepareConfigFile("state.yml")
if err != nil {
return err
}
appStateBytes, err := ioutil.ReadFile(filepath)
if err != nil {
return err
}
if len(appStateBytes) == 0 {
return yaml.Unmarshal(getDefaultAppState(), c.AppState)
}
return yaml.Unmarshal(appStateBytes, c.AppState)
}
2018-08-29 13:45:52 +02:00
// GetDefaultConfig returns the application default configuration
2018-08-26 10:42:25 +02:00
func GetDefaultConfig() []byte {
return []byte(
2018-08-27 10:01:05 +02:00
`gui:
## stuff relating to the UI
2018-08-26 10:42:25 +02:00
scrollHeight: 2
scrollPastBottom: true
2019-11-10 07:20:35 +02:00
mouseEvents: true
2018-08-26 10:42:25 +02:00
theme:
lightTheme: false
2018-08-26 10:42:25 +02:00
activeBorderColor:
- white
- bold
inactiveBorderColor:
- white
optionsTextColor:
- blue
commitLength:
show: true
git:
merging:
manualCommit: false
skipHookPrefix: 'WIP'
autoFetch: true
update:
method: prompt # can be: prompt | background | never
days: 14 # how often a update is checked for
2018-08-26 07:46:18 +02:00
reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined'
2019-11-10 13:07:45 +02:00
splashUpdatesIndex: 0
2018-09-05 19:56:11 +02:00
confirmOnQuit: false
2019-12-05 04:01:01 +02:00
keybinding:
universal:
quit: 'q'
quit-alt1: '<c-c>'
return: '<esc>'
quitWithoutChangingDirectory: 'Q'
togglePanel: '<tab>'
prevItem: '<up>'
nextItem: '<down>'
prevItem-alt: 'k'
nextItem-alt: 'j'
prevBlock: '<left>'
nextBlock: '<right>'
prevBlock-alt: 'h'
nextBlock-alt: 'l'
optionMenu: 'x'
optionMenu-alt1: '?'
select: '<space>'
2019-12-07 08:36:52 +02:00
goInto: '<enter>'
2019-12-05 04:01:01 +02:00
remove: 'd'
new: 'n'
edit: 'e'
openFile: 'o'
scrollUpMain: '<pgup>'
scrollDownMain: '<pgdown>'
scrollUpMain-alt1: 'K'
scrollDownMain-alt1: 'J'
scrollUpMain-alt2: '<c-u>'
scrollDownMain-alt2: '<c-d>'
2019-12-07 08:36:52 +02:00
executeCustomCommand: 'X'
2019-12-05 04:01:01 +02:00
createRebaseOptionsMenu: 'm'
pushFiles: 'P'
pullFiles: 'p'
refresh: 'R'
createPatchOptionsMenu: '<c-p>'
2019-12-07 08:36:52 +02:00
nextBranchTab: ']'
prevBranchTab: '['
2019-12-05 04:01:01 +02:00
status:
checkForUpdate: 'u'
2019-12-07 08:36:52 +02:00
recentRepos: '<enter>'
2019-12-05 04:01:01 +02:00
files:
commitChanges: 'c'
commitChangesWithoutHook: 'w'
amendLastCommit: 'A'
commitChangesWithEditor: 'C'
ignoreFile: 'i'
refreshFiles: 'r'
stashAllChanges: 's'
viewStashOptions: 'S'
toggleStagedAll: 'a'
viewResetOptions: 'D'
fetch: 'f'
branches:
createPullRequest: 'o'
2019-12-07 08:36:52 +02:00
checkoutBranchByName: 'c'
2019-12-05 04:01:01 +02:00
forceCheckoutBranch: 'F'
rebaseBranch: 'r'
mergeIntoCurrentBranch: 'M'
2019-12-07 08:36:52 +02:00
fastForward: 'f'
2019-12-05 04:01:01 +02:00
pushTag: 'P'
setUpstream: 'u'
2019-12-07 19:26:17 +02:00
fetchRemote: 'f'
2019-12-05 04:01:01 +02:00
commits:
squashDown: 's'
renameCommit: 'r'
renameCommitWithEditor: 'R'
2019-12-07 08:36:52 +02:00
viewResetOptions: 'g'
markCommitAsFixup: 'f'
2019-12-05 04:01:01 +02:00
createFixupCommit: 'F'
squashAboveCommits: 'S'
moveDownCommit: '<c-j>'
moveUpCommit: '<c-k>'
amendToCommit: 'A'
pickCommit: 'p'
revertCommit: 't'
cherryPickCopy: 'c'
cherryPickCopyRange: 'C'
pasteCommits: 'v'
tagCommit: 'T'
stash:
popStash: 'g'
commitFiles:
checkoutCommitFile: 'c'
main:
toggleDragSelect: 'v'
toggleDragSelect-alt: 'V'
toggleSelectHunk: 'a'
2020-01-06 17:37:21 +02:00
pickBothHunks: 'b'
2019-12-05 04:01:01 +02:00
undo: 'z'
2018-08-25 07:55:49 +02:00
`)
}
2018-08-25 07:55:49 +02:00
// AppState stores data between runs of the app like when the last update check
// was performed and which other repos have been checked out
type AppState struct {
2018-12-10 14:45:03 +02:00
LastUpdateCheck int64
RecentRepos []string
2018-08-25 07:55:49 +02:00
}
func getDefaultAppState() []byte {
2018-12-08 17:54:00 +02:00
return []byte(`
2018-12-07 16:46:49 +02:00
lastUpdateCheck: 0
recentRepos: []
`)
2018-08-15 13:34:25 +02:00
}
2018-08-18 05:54:39 +02:00
// // commenting this out until we use it again
// func homeDirectory() string {
// usr, err := user.Current()
// if err != nil {
// log.Fatal(err)
// }
// return usr.HomeDir
// }