1package config23import (4 "errors"5 "io/fs"6 "os"7 "os/exec"8 "path/filepath"9 "runtime"10 "strings"11)1213// Editor returns the command that opens path in the user's editor:14// $VISUAL, then $EDITOR (either may carry arguments, e.g. "code -w"),15// else vi, or notepad on Windows.16func Editor(path string, getenv func(string) string) (*exec.Cmd, error) {17 ed := getenv("VISUAL")18 if ed == "" {19 ed = getenv("EDITOR")20 }21 if ed == "" {22 ed = "vi"23 if runtime.GOOS == "windows" {24 ed = "notepad"25 }26 }27 argv, err := SplitCommand(ed)28 if err != nil {29 return nil, err30 }31 return exec.Command(argv[0], append(argv[1:], path)...), nil32}3334// EnsureFile creates the config file at path, with every option35// commented out at its default, when it doesn't exist yet.36func EnsureFile(path string) error {37 if _, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) {38 return err39 }40 if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {41 return err42 }43 return os.WriteFile(path, []byte(DefaultFile()), 0o600)44}4546// Tilde shortens a path in the home directory to ~/..., for messages.47func Tilde(path string) string {48 home, err := os.UserHomeDir()49 if err != nil || home == "" {50 return path51 }52 if rel, err := filepath.Rel(home, path); err == nil && rel != "." && !strings.HasPrefix(rel, "..") {53 return "~" + string(filepath.Separator) + rel54 }55 return path56}