Merge pull request #5640 from deltron-fr/fix/editor-flags

editutil: support editor commands with arguments
This commit is contained in:
Akihiro Suda authored and GitHub committed 2026-10-21 03:56:46 +09:00
commit f08bbaf533
4 files changed
+158 -7

No files matched your search

+22 -5
View File
@@ -23,11 +23,15 @@ package editorcmd
import (
"os"
"os/exec"
"github.com/mattn/go-shellwords"
)
// Detect detects a text editor command.
// Returns the editor exec path with arguments if provided.
//
// Returns an empty string when no editor is found.
func Detect() string {
func Detect() (editor string, args []string) {
candidates := []string{
os.Getenv("VISUAL"),
os.Getenv("EDITOR"),
@@ -40,10 +44,23 @@ func Detect() string {
if f == "" {
continue
}
x, err := exec.LookPath(f)
if err == nil {
return x
if path, err := exec.LookPath(f); err == nil {
return path, nil
}
parsedArgs, err := shellwords.Parse(f)
if err != nil || len(parsedArgs) == 0 {
continue
}
path, err := exec.LookPath(parsedArgs[0])
if err != nil {
continue
}
return path, parsedArgs[1:]
}
return ""
return "", nil
}
+87
View File
@@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
package editorcmd
import (
"os/exec"
"testing"
"gotest.tools/v3/assert"
)
func TestDetectEditorWithArguments(t *testing.T) {
t.Setenv("VISUAL", "")
t.Setenv("EDITOR", "go --wait")
expectedPath, err := exec.LookPath("go")
assert.NilError(t, err)
path, args := Detect()
assert.Equal(t, path, expectedPath)
assert.DeepEqual(t, args, []string{"--wait"})
}
func TestDetectQuotedEditor(t *testing.T) {
t.Setenv("VISUAL", "")
t.Setenv("EDITOR", `"go"`)
expectedPath, err := exec.LookPath("go")
assert.NilError(t, err)
path, args := Detect()
assert.Equal(t, path, expectedPath)
assert.Equal(t, len(args), 0)
}
func TestDetectQuotedEditorArgument(t *testing.T) {
t.Setenv("VISUAL", "")
t.Setenv("EDITOR", `go -ldflags "-s -w"`)
expectedPath, err := exec.LookPath("go")
assert.NilError(t, err)
path, args := Detect()
assert.Equal(t, path, expectedPath)
assert.DeepEqual(t, args, []string{"-ldflags", "-s -w"})
}
func TestDetectSkipsInvalidEditor(t *testing.T) {
t.Setenv("VISUAL", `go -c "unterminated`)
t.Setenv("EDITOR", "go --wait")
expectedPath, err := exec.LookPath("go")
assert.NilError(t, err)
path, args := Detect()
assert.Equal(t, path, expectedPath)
assert.DeepEqual(t, args, []string{"--wait"})
}
func TestDetectSkipsEmptyEditor(t *testing.T) {
t.Setenv("VISUAL", " ")
t.Setenv("EDITOR", "go --wait")
expectedPath, err := exec.LookPath("go")
assert.NilError(t, err)
path, args := Detect()
assert.Equal(t, path, expectedPath)
assert.DeepEqual(t, args, []string{"--wait"})
}
func TestDetectNoEditor(t *testing.T) {
t.Setenv("VISUAL", "")
t.Setenv("EDITOR", "")
t.Setenv("PATH", t.TempDir())
path, args := Detect()
assert.Equal(t, path, "")
assert.Equal(t, len(args), 0)
}
@@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: Copyright The Lima Authors
// SPDX-License-Identifier: Apache-2.0
//go:build windows
package editorcmd
import (
"fmt"
"os"
"path/filepath"
"testing"
"gotest.tools/v3/assert"
)
func TestDetectWindowsPath(t *testing.T) {
editorPath := makeWindowsEditor(t)
t.Setenv("VISUAL", "")
t.Setenv("EDITOR", editorPath)
path, args := Detect()
assert.Equal(t, path, editorPath)
assert.Equal(t, len(args), 0)
}
func TestDetectWindowsPathWithArguments(t *testing.T) {
editorPath := makeWindowsEditor(t)
t.Setenv("VISUAL", "")
t.Setenv("EDITOR", fmt.Sprintf(`'%s' --wait`, editorPath))
path, args := Detect()
assert.Equal(t, path, editorPath)
assert.DeepEqual(t, args, []string{"--wait"})
}
func makeWindowsEditor(t *testing.T) string {
t.Helper()
editorDir := filepath.Join(t.TempDir(), "editor with spaces")
assert.NilError(t, os.Mkdir(editorDir, 0o755))
editorPath := filepath.Join(editorDir, "editor.exe")
assert.NilError(t, os.WriteFile(editorPath, nil, 0o755))
return editorPath
}
+2 -2
View File
@@ -58,7 +58,7 @@ func GenerateEditorWarningHeader() string {
//
// OpenEditor returns nil when the file was saved as an empty file, optionally with whitespaces.
func OpenEditor(ctx context.Context, content []byte, hdr string) ([]byte, error) {
editor := editorcmd.Detect()
editor, args := editorcmd.Detect()
if editor == "" {
return nil, errors.New("could not detect a text editor binary, try setting $EDITOR")
}
@@ -76,7 +76,7 @@ func OpenEditor(ctx context.Context, content []byte, hdr string) ([]byte, error)
return nil, err
}
editorCmd := exec.CommandContext(ctx, editor, tmpYAMLPath)
editorCmd := exec.CommandContext(ctx, editor, append(args, tmpYAMLPath)...)
editorCmd.Env = os.Environ()
editorCmd.Stdin = os.Stdin
editorCmd.Stdout = os.Stdout