mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-28 09:08:41 +02:00
Merge branch 'master' into fix/tests
This commit is contained in:
commit
796f17eef4
@ -14,16 +14,44 @@
|
|||||||
- white
|
- white
|
||||||
optionsTextColor:
|
optionsTextColor:
|
||||||
- blue
|
- blue
|
||||||
git:
|
|
||||||
# stuff relating to git
|
|
||||||
os:
|
|
||||||
# stuff relating to the OS
|
|
||||||
update:
|
update:
|
||||||
method: prompt # can be: prompt | background | never
|
method: prompt # can be: prompt | background | never
|
||||||
days: 14 # how often an update is checked for
|
days: 14 # how often an update is checked for
|
||||||
reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined'
|
reporting: 'undetermined' # one of: 'on' | 'off' | 'undetermined'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Platform Defaults:
|
||||||
|
|
||||||
|
### Windows:
|
||||||
|
|
||||||
|
```
|
||||||
|
os:
|
||||||
|
openCommand: 'cmd /c "start "" {{filename}}"'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux:
|
||||||
|
|
||||||
|
```
|
||||||
|
os:
|
||||||
|
openCommand: 'bash -c \"xdg-open {{filename}} &>/dev/null &\"'
|
||||||
|
```
|
||||||
|
|
||||||
|
### OSX:
|
||||||
|
|
||||||
|
```
|
||||||
|
os:
|
||||||
|
openCommand: 'open {{filename}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recommended Config Values:
|
||||||
|
|
||||||
|
for users of VSCode
|
||||||
|
|
||||||
|
```
|
||||||
|
os:
|
||||||
|
openCommand: 'code -r {{filename}}'
|
||||||
|
```
|
||||||
|
|
||||||
## Color Attributes:
|
## Color Attributes:
|
||||||
|
|
||||||
For color attributes you can choose an array of attributes (with max one color attribute)
|
For color attributes you can choose an array of attributes (with max one color attribute)
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
<pre>
|
<pre>
|
||||||
<kbd>s</kbd>: squash down (only available for topmost commit)
|
<kbd>s</kbd>: squash down (only available for topmost commit)
|
||||||
<kbd>r</kbd>: rename commit
|
<kbd>r</kbd>: rename commit
|
||||||
|
<kbd>shift</kbd>+<kbd>R</kbd>: rename commit using git editor
|
||||||
<kbd>g</kbd>: reset to this commit
|
<kbd>g</kbd>: reset to this commit
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ func NewApp(config config.AppConfigurer) (*App, error) {
|
|||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
app.Log = newLogger(config)
|
app.Log = newLogger(config)
|
||||||
app.OSCommand = commands.NewOSCommand(app.Log)
|
app.OSCommand = commands.NewOSCommand(app.Log, config)
|
||||||
|
|
||||||
app.Tr = i18n.NewLocalizer(app.Log)
|
app.Tr = i18n.NewLocalizer(app.Log)
|
||||||
|
|
||||||
|
@ -422,6 +422,11 @@ func (c *GitCommand) PrepareCommitSubProcess() *exec.Cmd {
|
|||||||
return c.OSCommand.PrepareSubProcess("git", "commit")
|
return c.OSCommand.PrepareSubProcess("git", "commit")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrepareCommitAmendSubProcess prepares a subprocess for `git commit --amend --allow-empty`
|
||||||
|
func (c *GitCommand) PrepareCommitAmendSubProcess() *exec.Cmd {
|
||||||
|
return c.OSCommand.PrepareSubProcess("git", "commit", "--amend", "--allow-empty")
|
||||||
|
}
|
||||||
|
|
||||||
// GetBranchGraph gets the color-formatted graph of the log for the given branch
|
// GetBranchGraph gets the color-formatted graph of the log for the given branch
|
||||||
// Currently it limits the result to 100 commits, but when we get async stuff
|
// Currently it limits the result to 100 commits, but when we get async stuff
|
||||||
// working we can do lazy loading
|
// working we can do lazy loading
|
||||||
|
@ -189,6 +189,19 @@ func TestGitCommandStashSave(t *testing.T) {
|
|||||||
assert.NoError(t, gitCmd.StashSave("A stash message"))
|
assert.NoError(t, gitCmd.StashSave("A stash message"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGitCommandCommitAmend(t *testing.T) {
|
||||||
|
gitCmd := newDummyGitCommand()
|
||||||
|
gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd {
|
||||||
|
assert.EqualValues(t, "git", cmd)
|
||||||
|
assert.EqualValues(t, []string{"commit", "--amend", "--allow-empty"}, args)
|
||||||
|
|
||||||
|
return exec.Command("echo")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := gitCmd.PrepareCommitAmendSubProcess().CombinedOutput()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGitCommandMergeStatusFiles(t *testing.T) {
|
func TestGitCommandMergeStatusFiles(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
oldFiles []File
|
oldFiles []File
|
||||||
|
@ -6,7 +6,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
|
||||||
"github.com/mgutz/str"
|
"github.com/mgutz/str"
|
||||||
|
|
||||||
@ -20,22 +21,25 @@ type Platform struct {
|
|||||||
shell string
|
shell string
|
||||||
shellArg string
|
shellArg string
|
||||||
escapedQuote string
|
escapedQuote string
|
||||||
|
openCommand string
|
||||||
}
|
}
|
||||||
|
|
||||||
// OSCommand holds all the os commands
|
// OSCommand holds all the os commands
|
||||||
type OSCommand struct {
|
type OSCommand struct {
|
||||||
Log *logrus.Entry
|
Log *logrus.Entry
|
||||||
Platform *Platform
|
Platform *Platform
|
||||||
|
Config config.AppConfigurer
|
||||||
command func(string, ...string) *exec.Cmd
|
command func(string, ...string) *exec.Cmd
|
||||||
getGlobalGitConfig func(string) (string, error)
|
getGlobalGitConfig func(string) (string, error)
|
||||||
getenv func(string) string
|
getenv func(string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOSCommand os command runner
|
// NewOSCommand os command runner
|
||||||
func NewOSCommand(log *logrus.Entry) *OSCommand {
|
func NewOSCommand(log *logrus.Entry, config config.AppConfigurer) *OSCommand {
|
||||||
return &OSCommand{
|
return &OSCommand{
|
||||||
Log: log,
|
Log: log,
|
||||||
Platform: getPlatform(),
|
Platform: getPlatform(),
|
||||||
|
Config: config,
|
||||||
command: exec.Command,
|
command: exec.Command,
|
||||||
getGlobalGitConfig: gitconfig.Global,
|
getGlobalGitConfig: gitconfig.Global,
|
||||||
getenv: os.Getenv,
|
getenv: os.Getenv,
|
||||||
@ -47,7 +51,6 @@ func (c *OSCommand) RunCommandWithOutput(command string) (string, error) {
|
|||||||
c.Log.WithField("command", command).Info("RunCommand")
|
c.Log.WithField("command", command).Info("RunCommand")
|
||||||
splitCmd := str.ToArgv(command)
|
splitCmd := str.ToArgv(command)
|
||||||
c.Log.Info(splitCmd)
|
c.Log.Info(splitCmd)
|
||||||
|
|
||||||
return sanitisedCommandOutput(
|
return sanitisedCommandOutput(
|
||||||
c.command(splitCmd[0], splitCmd[1:]...).CombinedOutput(),
|
c.command(splitCmd[0], splitCmd[1:]...).CombinedOutput(),
|
||||||
)
|
)
|
||||||
@ -74,12 +77,9 @@ func (c *OSCommand) FileType(path string) string {
|
|||||||
// RunDirectCommand wrapper around direct commands
|
// RunDirectCommand wrapper around direct commands
|
||||||
func (c *OSCommand) RunDirectCommand(command string) (string, error) {
|
func (c *OSCommand) RunDirectCommand(command string) (string, error) {
|
||||||
c.Log.WithField("command", command).Info("RunDirectCommand")
|
c.Log.WithField("command", command).Info("RunDirectCommand")
|
||||||
args := str.ToArgv(c.Platform.shellArg + " " + command)
|
|
||||||
c.Log.Info(spew.Sdump(args))
|
|
||||||
|
|
||||||
return sanitisedCommandOutput(
|
return sanitisedCommandOutput(
|
||||||
exec.
|
c.command(c.Platform.shell, c.Platform.shellArg, command).
|
||||||
Command(c.Platform.shell, args...).
|
|
||||||
CombinedOutput(),
|
CombinedOutput(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -89,51 +89,24 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// errors like 'exit status 1' are not very useful so we'll create an error
|
// errors like 'exit status 1' are not very useful so we'll create an error
|
||||||
// from the combined output
|
// from the combined output
|
||||||
|
if outputString == "" {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return outputString, errors.New(outputString)
|
return outputString, errors.New(outputString)
|
||||||
}
|
}
|
||||||
return outputString, nil
|
return outputString, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOpenCommand get open command
|
|
||||||
func (c *OSCommand) getOpenCommand() (string, string, error) {
|
|
||||||
//NextStep open equivalents: xdg-open (linux), cygstart (cygwin), open (OSX)
|
|
||||||
trailMap := map[string]string{
|
|
||||||
"xdg-open": " &>/dev/null &",
|
|
||||||
"cygstart": "",
|
|
||||||
"open": "",
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, trail := range trailMap {
|
|
||||||
if err := c.RunCommand("which " + name); err == nil {
|
|
||||||
return name, trail, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", "", errors.New("Unsure what command to use to open this file")
|
|
||||||
}
|
|
||||||
|
|
||||||
// VsCodeOpenFile opens the file in code, with the -r flag to open in the
|
|
||||||
// current window
|
|
||||||
// each of these open files needs to have the same function signature because
|
|
||||||
// they're being passed as arguments into another function,
|
|
||||||
// but only editFile actually returns a *exec.Cmd
|
|
||||||
func (c *OSCommand) VsCodeOpenFile(filename string) (*exec.Cmd, error) {
|
|
||||||
return nil, c.RunCommand("code -r " + filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SublimeOpenFile opens the filein sublime
|
|
||||||
// may be deprecated in the future
|
|
||||||
func (c *OSCommand) SublimeOpenFile(filename string) (*exec.Cmd, error) {
|
|
||||||
return nil, c.RunCommand("subl " + filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenFile opens a file with the given
|
// OpenFile opens a file with the given
|
||||||
func (c *OSCommand) OpenFile(filename string) error {
|
func (c *OSCommand) OpenFile(filename string) error {
|
||||||
cmdName, cmdTrail, err := c.getOpenCommand()
|
commandTemplate := c.Config.GetUserConfig().GetString("os.openCommand")
|
||||||
if err != nil {
|
templateValues := map[string]string{
|
||||||
return err
|
"filename": c.Quote(filename),
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.RunCommand(cmdName + " " + c.Quote(filename) + cmdTrail) // TODO: test on linux
|
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
||||||
|
err := c.RunCommand(command)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditFile opens a file in a subprocess using whatever editor is available,
|
// EditFile opens a file in a subprocess using whatever editor is available,
|
||||||
|
@ -12,5 +12,6 @@ func getPlatform() *Platform {
|
|||||||
shell: "bash",
|
shell: "bash",
|
||||||
shellArg: "-c",
|
shellArg: "-c",
|
||||||
escapedQuote: "\"",
|
escapedQuote: "\"",
|
||||||
|
openCommand: "open {{filename}}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,28 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newDummyOSCommand() *OSCommand {
|
func newDummyOSCommand() *OSCommand {
|
||||||
return NewOSCommand(newDummyLog())
|
return NewOSCommand(newDummyLog(), newDummyAppConfig())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDummyAppConfig() *config.AppConfig {
|
||||||
|
appConfig := &config.AppConfig{
|
||||||
|
Name: "lazygit",
|
||||||
|
Version: "unversioned",
|
||||||
|
Commit: "",
|
||||||
|
BuildDate: "",
|
||||||
|
Debug: false,
|
||||||
|
BuildSource: "",
|
||||||
|
UserConfig: viper.New(),
|
||||||
|
}
|
||||||
|
_ = yaml.Unmarshal([]byte{}, appConfig.AppState)
|
||||||
|
return appConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOSCommandRunCommandWithOutput(t *testing.T) {
|
func TestOSCommandRunCommandWithOutput(t *testing.T) {
|
||||||
@ -59,44 +76,6 @@ func TestOSCommandRunCommand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOSCommandGetOpenCommand(t *testing.T) {
|
|
||||||
type scenario struct {
|
|
||||||
command func(string, ...string) *exec.Cmd
|
|
||||||
test func(string, string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios := []scenario{
|
|
||||||
{
|
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
|
||||||
return exec.Command("exit", "1")
|
|
||||||
},
|
|
||||||
func(name string, trail string, err error) {
|
|
||||||
assert.EqualError(t, err, "Unsure what command to use to open this file")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
|
||||||
assert.Equal(t, "which", name)
|
|
||||||
assert.Len(t, arg, 1)
|
|
||||||
assert.Regexp(t, "xdg-open|cygstart|open", arg[0])
|
|
||||||
return exec.Command("echo")
|
|
||||||
},
|
|
||||||
func(name string, trail string, err error) {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Regexp(t, "xdg-open|cygstart|open", name)
|
|
||||||
assert.Regexp(t, " \\&\\>/dev/null \\&|", trail)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range scenarios {
|
|
||||||
OSCmd := newDummyOSCommand()
|
|
||||||
OSCmd.command = s.command
|
|
||||||
|
|
||||||
s.test(OSCmd.getOpenCommand())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOSCommandOpenFile(t *testing.T) {
|
func TestOSCommandOpenFile(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
filename string
|
filename string
|
||||||
@ -111,29 +90,25 @@ func TestOSCommandOpenFile(t *testing.T) {
|
|||||||
return exec.Command("exit", "1")
|
return exec.Command("exit", "1")
|
||||||
},
|
},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
assert.EqualError(t, err, "Unsure what command to use to open this file")
|
assert.Error(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
func(name string, arg ...string) *exec.Cmd {
|
||||||
if name == "which" {
|
assert.Equal(t, "open", name)
|
||||||
return exec.Command("echo")
|
assert.Equal(t, []string{"test"}, arg)
|
||||||
}
|
return exec.Command("echo")
|
||||||
|
},
|
||||||
switch len(arg) {
|
func(err error) {
|
||||||
case 1:
|
assert.NoError(t, err)
|
||||||
assert.Regexp(t, "open|cygstart", name)
|
},
|
||||||
assert.EqualValues(t, "test", arg[0])
|
},
|
||||||
case 3:
|
{
|
||||||
assert.Equal(t, "xdg-open", name)
|
"filename with spaces",
|
||||||
assert.EqualValues(t, "test", arg[0])
|
func(name string, arg ...string) *exec.Cmd {
|
||||||
assert.Regexp(t, " \\&\\>/dev/null \\&|", arg[1])
|
assert.Equal(t, "open", name)
|
||||||
assert.EqualValues(t, "&", arg[2])
|
assert.Equal(t, []string{"filename with spaces"}, arg)
|
||||||
default:
|
|
||||||
assert.Fail(t, "Unexisting command given")
|
|
||||||
}
|
|
||||||
|
|
||||||
return exec.Command("echo")
|
return exec.Command("echo")
|
||||||
},
|
},
|
||||||
func(err error) {
|
func(err error) {
|
||||||
@ -145,6 +120,7 @@ func TestOSCommandOpenFile(t *testing.T) {
|
|||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
OSCmd := newDummyOSCommand()
|
OSCmd := newDummyOSCommand()
|
||||||
OSCmd.command = s.command
|
OSCmd.command = s.command
|
||||||
|
OSCmd.Config.GetUserConfig().Set("os.openCommand", "open {{filename}}")
|
||||||
|
|
||||||
s.test(OSCmd.OpenFile(s.filename))
|
s.test(OSCmd.OpenFile(s.filename))
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,6 @@ func getPlatform() *Platform {
|
|||||||
os: "windows",
|
os: "windows",
|
||||||
shell: "cmd",
|
shell: "cmd",
|
||||||
shellArg: "/c",
|
shellArg: "/c",
|
||||||
escapedQuote: "\\\"",
|
escapedQuote: `\"`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,7 @@ type AppConfigurer interface {
|
|||||||
|
|
||||||
// NewAppConfig makes a new app config
|
// NewAppConfig makes a new app config
|
||||||
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag *bool) (*AppConfig, error) {
|
func NewAppConfig(name, version, commit, date string, buildSource string, debuggingFlag *bool) (*AppConfig, error) {
|
||||||
defaultConfig := GetDefaultConfig()
|
userConfig, err := LoadConfig("config", true)
|
||||||
userConfig, err := LoadConfig("config", defaultConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -113,13 +112,16 @@ func newViper(filename string) (*viper.Viper, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig gets the user's config
|
// LoadConfig gets the user's config
|
||||||
func LoadConfig(filename string, defaults []byte) (*viper.Viper, error) {
|
func LoadConfig(filename string, withDefaults bool) (*viper.Viper, error) {
|
||||||
v, err := newViper(filename)
|
v, err := newViper(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if defaults != nil {
|
if withDefaults {
|
||||||
if err = LoadDefaults(v, defaults); err != nil {
|
if err = LoadDefaults(v, GetDefaultConfig()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = LoadDefaults(v, GetPlatformDefaultConfig()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +133,7 @@ func LoadConfig(filename string, defaults []byte) (*viper.Viper, error) {
|
|||||||
|
|
||||||
// LoadDefaults loads in the defaults defined in this file
|
// LoadDefaults loads in the defaults defined in this file
|
||||||
func LoadDefaults(v *viper.Viper, defaults []byte) error {
|
func LoadDefaults(v *viper.Viper, defaults []byte) error {
|
||||||
return v.ReadConfig(bytes.NewBuffer(defaults))
|
return v.MergeConfig(bytes.NewBuffer(defaults))
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareConfigFile(filename string) (string, error) {
|
func prepareConfigFile(filename string) (string, error) {
|
||||||
@ -166,7 +168,7 @@ func LoadAndMergeFile(v *viper.Viper, filename string) error {
|
|||||||
func (c *AppConfig) WriteToUserConfig(key, value string) error {
|
func (c *AppConfig) WriteToUserConfig(key, value string) error {
|
||||||
// reloading the user config directly (without defaults) so that we're not
|
// reloading the user config directly (without defaults) so that we're not
|
||||||
// writing any defaults back to the user's config
|
// writing any defaults back to the user's config
|
||||||
v, err := LoadConfig("config", nil)
|
v, err := LoadConfig("config", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -220,10 +222,6 @@ func GetDefaultConfig() []byte {
|
|||||||
- white
|
- white
|
||||||
optionsTextColor:
|
optionsTextColor:
|
||||||
- blue
|
- blue
|
||||||
git:
|
|
||||||
# stuff relating to git
|
|
||||||
os:
|
|
||||||
# stuff relating to the OS
|
|
||||||
update:
|
update:
|
||||||
method: prompt # can be: prompt | background | never
|
method: prompt # can be: prompt | background | never
|
||||||
days: 14 # how often a update is checked for
|
days: 14 # how often a update is checked for
|
||||||
|
10
pkg/config/config_default_platform.go
Normal file
10
pkg/config/config_default_platform.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// +build !windows,!linux
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||||
|
func GetPlatformDefaultConfig() []byte {
|
||||||
|
return []byte(
|
||||||
|
`os:
|
||||||
|
openCommand: 'open {{filename}}'`)
|
||||||
|
}
|
8
pkg/config/config_linux.go
Normal file
8
pkg/config/config_linux.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||||
|
func GetPlatformDefaultConfig() []byte {
|
||||||
|
return []byte(
|
||||||
|
`os:
|
||||||
|
openCommand: 'bash -c \"xdg-open {{filename}} &>/dev/null &\"'`)
|
||||||
|
}
|
8
pkg/config/config_windows.go
Normal file
8
pkg/config/config_windows.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||||
|
func GetPlatformDefaultConfig() []byte {
|
||||||
|
return []byte(
|
||||||
|
`os:
|
||||||
|
openCommand: 'cmd /c "start "" {{filename}}"'`)
|
||||||
|
}
|
@ -155,6 +155,19 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if gui.getItemPosition(v) != 0 {
|
||||||
|
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.SubProcess = gui.GitCommand.PrepareCommitAmendSubProcess()
|
||||||
|
g.Update(func(g *gocui.Gui) error {
|
||||||
|
return gui.Errors.ErrSubProcess
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) getSelectedCommit(g *gocui.Gui) (commands.Commit, error) {
|
func (gui *Gui) getSelectedCommit(g *gocui.Gui) (commands.Commit, error) {
|
||||||
v, err := g.View("commits")
|
v, err := g.View("commits")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
// "strings"
|
// "strings"
|
||||||
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@ -250,11 +249,10 @@ func (gui *Gui) PrepareSubProcess(g *gocui.Gui, commands ...string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) genericFileOpen(g *gocui.Gui, v *gocui.View, filename string, open func(string) (*exec.Cmd, error)) error {
|
func (gui *Gui) editFile(filename string) error {
|
||||||
|
sub, err := gui.OSCommand.EditFile(filename)
|
||||||
sub, err := open(filename)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gui.createErrorPanel(g, err.Error())
|
return gui.createErrorPanel(gui.g, err.Error())
|
||||||
}
|
}
|
||||||
if sub != nil {
|
if sub != nil {
|
||||||
gui.SubProcess = sub
|
gui.SubProcess = sub
|
||||||
@ -268,7 +266,8 @@ func (gui *Gui) handleFileEdit(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return gui.genericFileOpen(g, v, file.Name, gui.OSCommand.EditFile)
|
|
||||||
|
return gui.editFile(file.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
||||||
@ -279,22 +278,6 @@ func (gui *Gui) handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return gui.openFile(file.Name)
|
return gui.openFile(file.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleSublimeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
file, err := gui.getSelectedFile(g)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gui.genericFileOpen(g, v, file.Name, gui.OSCommand.SublimeOpenFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleVsCodeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
file, err := gui.getSelectedFile(g)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gui.genericFileOpen(g, v, file.Name, gui.OSCommand.VsCodeOpenFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gui.refreshFiles(g)
|
return gui.refreshFiles(g)
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,6 @@ func (gui *Gui) keybindings(g *gocui.Gui) error {
|
|||||||
{ViewName: "files", Key: 'm', Modifier: gocui.ModNone, Handler: gui.handleSwitchToMerge},
|
{ViewName: "files", Key: 'm', Modifier: gocui.ModNone, Handler: gui.handleSwitchToMerge},
|
||||||
{ViewName: "files", Key: 'e', Modifier: gocui.ModNone, Handler: gui.handleFileEdit},
|
{ViewName: "files", Key: 'e', Modifier: gocui.ModNone, Handler: gui.handleFileEdit},
|
||||||
{ViewName: "files", Key: 'o', Modifier: gocui.ModNone, Handler: gui.handleFileOpen},
|
{ViewName: "files", Key: 'o', Modifier: gocui.ModNone, Handler: gui.handleFileOpen},
|
||||||
{ViewName: "files", Key: 's', Modifier: gocui.ModNone, Handler: gui.handleSublimeFileOpen},
|
|
||||||
{ViewName: "files", Key: 'v', Modifier: gocui.ModNone, Handler: gui.handleVsCodeFileOpen},
|
|
||||||
{ViewName: "files", Key: 'i', Modifier: gocui.ModNone, Handler: gui.handleIgnoreFile},
|
{ViewName: "files", Key: 'i', Modifier: gocui.ModNone, Handler: gui.handleIgnoreFile},
|
||||||
{ViewName: "files", Key: 'r', Modifier: gocui.ModNone, Handler: gui.handleRefreshFiles},
|
{ViewName: "files", Key: 'r', Modifier: gocui.ModNone, Handler: gui.handleRefreshFiles},
|
||||||
{ViewName: "files", Key: 'S', Modifier: gocui.ModNone, Handler: gui.handleStashSave},
|
{ViewName: "files", Key: 'S', Modifier: gocui.ModNone, Handler: gui.handleStashSave},
|
||||||
@ -64,6 +62,7 @@ func (gui *Gui) keybindings(g *gocui.Gui) error {
|
|||||||
{ViewName: "branches", Key: 'm', Modifier: gocui.ModNone, Handler: gui.handleMerge},
|
{ViewName: "branches", Key: 'm', Modifier: gocui.ModNone, Handler: gui.handleMerge},
|
||||||
{ViewName: "commits", Key: 's', Modifier: gocui.ModNone, Handler: gui.handleCommitSquashDown},
|
{ViewName: "commits", Key: 's', Modifier: gocui.ModNone, Handler: gui.handleCommitSquashDown},
|
||||||
{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: gui.handleRenameCommit},
|
{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: gui.handleRenameCommit},
|
||||||
|
{ViewName: "commits", Key: 'R', Modifier: gocui.ModNone, Handler: gui.handleRenameCommitEditor},
|
||||||
{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: gui.handleResetToCommit},
|
{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: gui.handleResetToCommit},
|
||||||
{ViewName: "commits", Key: 'f', Modifier: gocui.ModNone, Handler: gui.handleCommitFixup},
|
{ViewName: "commits", Key: 'f', Modifier: gocui.ModNone, Handler: gui.handleCommitFixup},
|
||||||
{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleStashApply},
|
{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleStashApply},
|
||||||
|
@ -76,7 +76,7 @@ func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
|
|||||||
|
|
||||||
func (gui *Gui) handleEditConfig(g *gocui.Gui, v *gocui.View) error {
|
func (gui *Gui) handleEditConfig(g *gocui.Gui, v *gocui.View) error {
|
||||||
filename := gui.Config.GetUserConfig().ConfigFileUsed()
|
filename := gui.Config.GetUserConfig().ConfigFileUsed()
|
||||||
return gui.genericFileOpen(g, v, filename, gui.OSCommand.EditFile)
|
return gui.editFile(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
func lazygitTitle() string {
|
func lazygitTitle() string {
|
||||||
|
@ -91,3 +91,11 @@ func Loader() string {
|
|||||||
index := nanos / 50000000 % int64(len(characters))
|
index := nanos / 50000000 % int64(len(characters))
|
||||||
return characters[index : index+1]
|
return characters[index : index+1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolvePlaceholderString populates a template with values
|
||||||
|
func ResolvePlaceholderString(str string, arguments map[string]string) string {
|
||||||
|
for key, value := range arguments {
|
||||||
|
str = strings.Replace(str, "{{"+key+"}}", value, -1)
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
@ -114,3 +114,56 @@ func TestNormalizeLinefeeds(t *testing.T) {
|
|||||||
assert.EqualValues(t, string(s.expected), NormalizeLinefeeds(string(s.byteArray)))
|
assert.EqualValues(t, string(s.expected), NormalizeLinefeeds(string(s.byteArray)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolvePlaceholderString(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
templateString string
|
||||||
|
arguments map[string]string
|
||||||
|
expected string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hello",
|
||||||
|
map[string]string{},
|
||||||
|
"hello",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hello {{arg}}",
|
||||||
|
map[string]string{},
|
||||||
|
"hello {{arg}}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hello {{arg}}",
|
||||||
|
map[string]string{"arg": "there"},
|
||||||
|
"hello there",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hello",
|
||||||
|
map[string]string{"arg": "there"},
|
||||||
|
"hello",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"{{nothing}}",
|
||||||
|
map[string]string{"nothing": ""},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"{{}} {{ this }} { should not throw}} an {{{{}}}} error",
|
||||||
|
map[string]string{
|
||||||
|
"blah": "blah",
|
||||||
|
"this": "won't match",
|
||||||
|
},
|
||||||
|
"{{}} {{ this }} { should not throw}} an {{{{}}}} error",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
assert.EqualValues(t, string(s.expected), ResolvePlaceholderString(s.templateString, s.arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user