From 7ba2b450712dbdf5d2fe5bc1d3c27dc6756d3b85 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Fri, 15 Dec 2023 14:10:16 -0300 Subject: [PATCH] feat: improve editor open at line (#26) * feat: improve editor open at line refs #17 * fix: improve code * test: fix option name Signed-off-by: Carlos Alexandro Becker --------- Signed-off-by: Carlos Alexandro Becker --- editor/editor.go | 59 +++++++++++++++++++++++++------------------ editor/editor_test.go | 20 +++++++++++++++ 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/editor/editor.go b/editor/editor.go index 311f2ef7..c40ec5c8 100644 --- a/editor/editor.go +++ b/editor/editor.go @@ -4,51 +4,60 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "strings" ) const defaultEditor = "nano" -// Get editors that have support for the `+[line number]` flag -// to edit the file at the current line that is in the pager -func getEditorsWithLineNumberSupport() []string { - return []string{ - "vi", - "vim", - "nvim", - "nano", - } -} +// Option defines an editor option. +// +// An Option may act differently in some editors, or not be supported in +// some of them. +type Option func(editor, filename string) (args []string, pathInArgs bool) -func hasLineNumberSupport(editor string) bool { - for _, supportedEditor := range getEditorsWithLineNumberSupport() { - if editor == supportedEditor { - return true +// OpenAtLine opens the file at the given line number in supported editors. +func OpenAtLine(number uint) Option { + plusLineEditors := []string{"vi", "vim", "nvim", "nano"} + return func(editor, filename string) ([]string, bool) { + for _, e := range plusLineEditors { + if editor == e { + return []string{fmt.Sprintf("+%d", number)}, false + } } + if editor == "code" { + return []string{ + "--goto", + fmt.Sprintf("%s:%d", filename, number), + }, true + } + return nil, false } - return false } // Cmd returns a *exec.Cmd editing the given path with $EDITOR or nano if no // $EDITOR is set. -func Cmd(app, path string, lineNumber_optional ...uint) (*exec.Cmd, error) { +func Cmd(app, path string, options ...Option) (*exec.Cmd, error) { if os.Getenv("SNAP_REVISION") != "" { return nil, fmt.Errorf("Did you install with Snap? %[1]s is sandboxed and unable to open an editor. Please install %[1]s with Go or another package manager to enable editing.", app) } editor, args := getEditor() + editorName := filepath.Base(editor) - // Add line number to open the editor at if provided and a supported editor is being used - if len(lineNumber_optional) == 1 && hasLineNumberSupport(editor) { - lineNumber := lineNumber_optional[0] - - lineNumberArg := fmt.Sprintf("+%d", lineNumber) - // Insert line position arg before file name and other flags (required for nano) - args = append([]string{lineNumberArg}, args...) - + needsToAppendPath := true + for _, opt := range options { + optArgs, pathInArgs := opt(editorName, path) + if pathInArgs { + needsToAppendPath = false + } + args = append(args, optArgs...) + } + if needsToAppendPath { + args = append(args, path) } - return exec.Command(editor, append(args, path)...), nil + return exec.Command(editor, args...), nil } func getEditor() (string, []string) { diff --git a/editor/editor_test.go b/editor/editor_test.go index 6de2083a..84f87747 100644 --- a/editor/editor_test.go +++ b/editor/editor_test.go @@ -24,6 +24,26 @@ func TestEditor(t *testing.T) { }) } + t.Run("with line number", func(t *testing.T) { + for k, v := range map[string][]string{ + "": {"nano", "+12", filename}, + "nvim": {"nvim", "+12", filename}, + "vim": {"vim", "+12", filename}, + "vscode --foo": {"vscode", "--foo", filename}, + "nvim -a -b": {"nvim", "-a", "-b", "+12", filename}, + "code --foo": {"code", "--foo", "--goto", filename + ":12"}, + } { + t.Run(k, func(t *testing.T) { + t.Setenv("EDITOR", k) + cmd, _ := Cmd("X", "README.md", OpenAtLine(12)) + got := cmd.Args + if !reflect.DeepEqual(got, v) { + t.Fatalf("expected %v; got %v", v, got) + } + }) + } + }) + t.Run("inside snap", func(t *testing.T) { t.Setenv("SNAP_REVISION", "10") got, err := Cmd("X", "foo")