diff --git a/src/core.go b/src/core.go index f0728f806a4..ec25d1d0c1b 100644 --- a/src/core.go +++ b/src/core.go @@ -3,7 +3,6 @@ package fzf import ( "os" - "os/exec" "sync" "time" @@ -25,10 +24,8 @@ func Run(opts *Options) (int, error) { return runTmux(os.Args, opts) } - if os.Getenv("TERM_PROGRAM") == "mintty" && !opts.NoWinpty { - if _, err := exec.LookPath("winpty"); err == nil { - return runWinpty(os.Args, opts) - } + if needWinpty(opts) { + return runWinpty(os.Args, opts) } if err := postProcessOptions(opts); err != nil { diff --git a/src/util/util.go b/src/util/util.go index b5b27f2848a..9b926b8d57c 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -3,6 +3,7 @@ package util import ( "math" "os" + "strconv" "strings" "time" @@ -189,3 +190,34 @@ func ToKebabCase(s string) string { } return strings.ToLower(name) } + +// CompareVersions compares two version strings +func CompareVersions(v1, v2 string) int { + parts1 := strings.Split(v1, ".") + parts2 := strings.Split(v2, ".") + + atoi := func(s string) int { + n, e := strconv.Atoi(s) + if e != nil { + return 0 + } + return n + } + + for i := 0; i < Max(len(parts1), len(parts2)); i++ { + var p1, p2 int + if i < len(parts1) { + p1 = atoi(parts1[i]) + } + if i < len(parts2) { + p2 = atoi(parts2[i]) + } + + if p1 > p2 { + return 1 + } else if p1 < p2 { + return -1 + } + } + return 0 +} diff --git a/src/util/util_test.go b/src/util/util_test.go index af0762b5ed4..013f3c239e3 100644 --- a/src/util/util_test.go +++ b/src/util/util_test.go @@ -203,3 +203,34 @@ func TestStringWidth(t *testing.T) { t.Errorf("Expected: %d, Actual: %d", 1, w) } } + +func TestCompareVersions(t *testing.T) { + assert := func(a, b string, expected int) { + if result := CompareVersions(a, b); result != expected { + t.Errorf("Expected: %d, Actual: %d", expected, result) + } + } + + assert("2", "1", 1) + assert("2", "2", 0) + assert("2", "10", -1) + + assert("2.1", "2.2", -1) + assert("2.1", "2.1.1", -1) + + assert("1.2.3", "1.2.2", 1) + assert("1.2.3", "1.2.3", 0) + assert("1.2.3", "1.2.3.0", 0) + assert("1.2.3", "1.2.4", -1) + + // Different number of parts + assert("1.0.0", "1", 0) + assert("1.0.0", "1.0", 0) + assert("1.0.0", "1.0.0", 0) + assert("1.0", "1.0.0", 0) + assert("1", "1.0.0", 0) + assert("1.0.0", "1.0.0.1", -1) + assert("1.0.0.1.0", "1.0.0.1", 0) + + assert("", "3.4.5", -1) +} diff --git a/src/winpty.go b/src/winpty.go index 46f9400fc44..adee3b0b74f 100644 --- a/src/winpty.go +++ b/src/winpty.go @@ -4,6 +4,10 @@ package fzf import "errors" +func needWinpty(_ *Options) bool { + return false +} + func runWinpty(_ []string, _ *Options) (int, error) { return ExitError, errors.New("Not supported") } diff --git a/src/winpty_windows.go b/src/winpty_windows.go index 83802a2bf99..6d7e366fd89 100644 --- a/src/winpty_windows.go +++ b/src/winpty_windows.go @@ -6,8 +6,46 @@ import ( "fmt" "os" "os/exec" + + "github.com/junegunn/fzf/src/util" ) +func isMintty345() bool { + return util.CompareVersions(os.Getenv("TERM_PROGRAM_VERSION"), "3.4.5") >= 0 +} + +func needWinpty(opts *Options) bool { + if os.Getenv("TERM_PROGRAM") != "mintty" { + return false + } + if isMintty345() { + /* + See: https://github.com/junegunn/fzf/issues/3809 + + "MSYS=enable_pcon" allows fzf to run properly on mintty 3.4.5 or later, + however `--height` option still doesn't work, so let's just disable it. + + We're not going to worry too much about restoring the original value. + */ + if os.Getenv("MSYS") == "enable_pcon" { + opts.Height = heightSpec{} + return false + } + + // Setting the environment variable here unfortunately doesn't help, + // so we need to start a child process with "MSYS=enable_pcon" + // os.Setenv("MSYS", "enable_pcon") + return true + } + if _, err := exec.LookPath("winpty"); err != nil { + return false + } + if opts.NoWinpty { + return false + } + return true +} + func runWinpty(args []string, opts *Options) (int, error) { sh, err := sh() if err != nil { @@ -20,6 +58,17 @@ func runWinpty(args []string, opts *Options) (int, error) { } argStr += ` --no-winpty --no-height` + if isMintty345() { + return runProxy(argStr, func(temp string) *exec.Cmd { + cmd := exec.Command(sh, temp) + cmd.Env = append(os.Environ(), "MSYS=enable_pcon") + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd + }, opts, false) + } + return runProxy(argStr, func(temp string) *exec.Cmd { cmd := exec.Command(sh, "-c", fmt.Sprintf(`winpty < /dev/tty > /dev/tty -- sh %q`, temp)) cmd.Stdout = os.Stdout