graphql-engine/cli/internal/metadataobject/actions/editor/editor.go
Kali Vara Purushotham Santhati b875d95a75 cli: separate the metadata operations from migrate driver
Co-authored-by: Aravind K P <8335904+scriptonist@users.noreply.github.com>
GitOrigin-RevId: 8f6791d5fd51d011623361ac2f5994c8badb50bf
2021-04-01 05:14:17 +00:00

126 lines
2.9 KiB
Go

package editor
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
const (
defaultEditor = "vi"
defaultShell = "/bin/bash"
windowsEditor = "notepad"
windowsShell = "cmd"
)
// PreferredEditorResolver is a function that returns an editor that the user
// prefers to use, such as the configured `$EDITOR` environment variable.
type PreferredEditorResolver func() ([]string, bool)
// GetPreferredEditorFromEnvironment returns the user's editor as defined by the
// `$EDITOR` environment variable, or the `DefaultEditor` if it is not set.
func GetPreferredEditorFromEnvironment() ([]string, bool) {
editor := os.Getenv("EDITOR")
if len(editor) == 0 {
editor = platformize(defaultEditor, windowsEditor)
}
if !strings.Contains(editor, " ") {
return []string{editor}, false
}
if !strings.ContainsAny(editor, "\"'\\") {
return strings.Split(editor, " "), false
}
shell := defaultEnvShell()
return append(shell, editor), true
}
// OpenFileInEditor opens filename in a text editor.
func OpenFileInEditor(filename string, resolveEditor PreferredEditorResolver) error {
// Get the full executable path for the editor.
args, shell := resolveEditor()
if len(args) == 0 {
return fmt.Errorf("no editor defined, can't open %s", filename)
}
abs, err := filepath.Abs(filename)
if err != nil {
return err
}
cmdArgs := make([]string, len(args))
copy(cmdArgs, args)
if shell {
last := cmdArgs[len(cmdArgs)-1]
cmdArgs[len(cmdArgs)-1] = fmt.Sprintf("%s %q", last, abs)
} else {
cmdArgs = append(cmdArgs, abs)
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// CaptureInputFromEditor opens a temporary file in a text editor and returns
// the written bytes on success or an error on failure. It handles deletion
// of the temporary file behind the scenes.
func CaptureInputFromEditor(resolveEditor PreferredEditorResolver, text, extension string) ([]byte, error) {
file, err := ioutil.TempFile(os.TempDir(), fmt.Sprintf("*.%s", extension))
if err != nil {
return []byte{}, err
}
filename := file.Name()
// Defer removal of the temporary file in case any of the next steps fail.
defer os.Remove(filename)
_, err = file.Write([]byte(text))
if err != nil {
return []byte{}, err
}
if err = file.Close(); err != nil {
return []byte{}, err
}
if err = OpenFileInEditor(filename, resolveEditor); err != nil {
return []byte{}, err
}
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return []byte{}, err
}
return bytes, nil
}
func defaultEnvShell() []string {
shell := os.Getenv("SHELL")
if len(shell) == 0 {
shell = platformize(defaultShell, windowsShell)
}
flag := "-c"
if shell == windowsShell {
flag = "/C"
}
return []string{shell, flag}
}
func platformize(linux, windows string) string {
if runtime.GOOS == "windows" {
return windows
}
return linux
}