Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce new subcmd edit #278

Merged
merged 7 commits into from
Apr 26, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CMDREF.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ Command
This is shortcut of:
volt profile rm -current {repository} [{repository2} ...]

edit [-e|--editor {editor}] {repository} [{repository2} ...]
Open the plugconf file(s) of one or more {repository} for editing.

profile set {name}
Set profile name

Expand Down Expand Up @@ -112,6 +115,23 @@ Description
volt profile rm {current profile} {repository} [{repository2} ...]
```

# volt edit

```
Usage
volt edit [-help] [-e|--editor {editor}] {repository} [{repository2} ...]

Quick example
$ volt edit tyru/caw.vim # will open the plugconf file for tyru/caw.vim for editing

Description
Open the plugconf file(s) of one or more {repository} for editing.

If the -e option was given, use the given editor for editing those files (unless it cannot be found)

It also calls "volt build" afterwards if modifications were made to the plugconf file(s).
```

# volt enable

```
Expand Down
12 changes: 12 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Config struct {
Alias map[string][]string `toml:"alias"`
Build configBuild `toml:"build"`
Get configGet `toml:"get"`
Edit configEdit `toml:"edit"`
}

// configBuild is a config for 'volt build'.
Expand All @@ -25,6 +26,11 @@ type configGet struct {
FallbackGitCmd *bool `toml:"fallback_git_cmd"`
}

// configEdit is a config for 'volt edit'.
type configEdit struct {
Editor string `toml:"editor"`
}

const (
// SymlinkBuilder creates symlinks when 'volt build'.
SymlinkBuilder = "symlink"
Expand All @@ -43,6 +49,9 @@ func initialConfigTOML() *Config {
CreateSkeletonPlugconf: &trueValue,
FallbackGitCmd: &falseValue,
},
Edit: configEdit{
Editor: "",
},
}
}

Expand Down Expand Up @@ -76,6 +85,9 @@ func merge(cfg, initCfg *Config) {
if cfg.Get.FallbackGitCmd == nil {
cfg.Get.FallbackGitCmd = initCfg.Get.FallbackGitCmd
}
if cfg.Edit.Editor == "" {
cfg.Edit.Editor = initCfg.Edit.Editor
}
}

func validate(cfg *Config) error {
Expand Down
218 changes: 218 additions & 0 deletions subcmd/edit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package subcmd

import (
"errors"
"flag"
"fmt"
"os"
"os/exec"

"github.com/vim-volt/volt/config"
"github.com/vim-volt/volt/lockjson"
"github.com/vim-volt/volt/logger"
"github.com/vim-volt/volt/pathutil"
"github.com/vim-volt/volt/subcmd/builder"
)

func init() {
cmdMap["edit"] = &editCmd{}
}

type editCmd struct {
helped bool
editor string
}

func (cmd *editCmd) ProhibitRootExecution(args []string) bool { return true }

func (cmd *editCmd) FlagSet() *flag.FlagSet {
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
fs.SetOutput(os.Stdout)
fs.Usage = func() {
fmt.Print(`
Usage
volt edit [-help] [-e|--editor {editor}] {repository} [{repository2} ...]

Quick example
$ volt edit tyru/caw.vim # will open the plugconf file for tyru/caw.vim for editing

Description
Open the plugconf file(s) of one or more {repository} for editing.

If the -e option was given, use the given editor for editing those files (unless it cannot be found)

It also calls "volt build" afterwards if modifications were made to the plugconf file(s).` + "\n\n")
//fmt.Println("Options")
//fs.PrintDefaults()
fmt.Println()
cmd.helped = true
}
fs.StringVar(&cmd.editor, "editor", "", "Use the given editor for editing the plugconf files")
fs.StringVar(&cmd.editor, "e", "", "Use the given editor for editing the plugconf files")
return fs
}

func (cmd *editCmd) Run(args []string) *Error {
reposPathList, err := cmd.parseArgs(args)
if err == ErrShowedHelp {
return nil
}
if err != nil {
return &Error{Code: 10, Msg: "Failed to parse args: " + err.Error()}
}

hasChanges, err := cmd.doEdit(reposPathList)
if err != nil {
//FIXME: Which error code to use?
hupfdule marked this conversation as resolved.
Show resolved Hide resolved
return &Error{Code: 15, Msg: "Failed to edit plugconf file: " + err.Error()}
}

// Build opt dir
if hasChanges {
err = builder.Build(false)
if err != nil {
return &Error{Code: 12, Msg: "Could not build " + pathutil.VimVoltDir() + ": " + err.Error()}
}
}

return nil
}

func (cmd *editCmd) doEdit(reposPathList []pathutil.ReposPath) (bool, error) {
// Read lock.json
lockJSON, err := lockjson.Read()
if err != nil {
return false, err
}

// Read config.toml
cfg, err := config.Read()
if err != nil {
return false, errors.New("could not read config.toml: " + err.Error())
}

viitor, err := cmd.identifyEditor(cfg)
hupfdule marked this conversation as resolved.
Show resolved Hide resolved
if err != nil || viitor == "" {
//FIXME: Which error code to use?
return false, &Error{Code: 30, Msg: "No usable viitor found"}
}

changeWasMade := false
//FIXME: Run single vim instance? Leads to problems if the configured
//editor does not support to open multiple files
hupfdule marked this conversation as resolved.
Show resolved Hide resolved
for _, reposPath := range reposPathList {

// Edit plugconf file
plugconfPath := reposPath.Plugconf()

// Install a new template if none exists
if !pathutil.Exists(plugconfPath) {
getCmd := new(getCmd)
logger.Debugf("Installing new plugconf for '%s'.", reposPath)
getCmd.downloadPlugconf(reposPath)
}

// Remember modification time before opening the editor
info, err := os.Stat(plugconfPath)
if err != nil {
return false, err
}
mTimeBefore := info.ModTime()

// Call the editor with the plugconf file
vimCmd := exec.Command(viitor, plugconfPath)
hupfdule marked this conversation as resolved.
Show resolved Hide resolved
vimCmd.Stdin = os.Stdin
vimCmd.Stdout = os.Stdout
if err = vimCmd.Run(); err != nil {
//FIXME: Don't abort immediately, but try to edit remaining files?
hupfdule marked this conversation as resolved.
Show resolved Hide resolved
return false, err
}

// Get modification time after closing the editor
info, err = os.Stat(plugconfPath)
if err != nil {
return false, err
}
mTimeAfter := info.ModTime()

// A change was made if the modification time was updated
changeWasMade = changeWasMade || mTimeAfter.After(mTimeBefore)

// Remove repository from lock.json
err = lockJSON.Repos.RemoveAllReposPath(reposPath)
err2 := lockJSON.Profiles.RemoveAllReposPath(reposPath)
if err == nil || err2 == nil {
// ignore?
}
}

// Write to lock.json
if err = lockJSON.Write(); err != nil {
return changeWasMade, err
}
return changeWasMade, nil
}

func (cmd *editCmd) parseArgs(args []string) (pathutil.ReposPathList, error) {
fs := cmd.FlagSet()
fs.Parse(args)
if cmd.helped {
return nil, ErrShowedHelp
}

if len(fs.Args()) == 0 {
fs.Usage()
return nil, errors.New("repository was not given")
}

// Normalize repos path
reposPathList := make(pathutil.ReposPathList, 0, len(fs.Args()))
for _, arg := range fs.Args() {
reposPath, err := pathutil.NormalizeRepos(arg)
if err != nil {
return nil, err
}
reposPathList = append(reposPathList, reposPath)
}

return reposPathList, nil
}

func (cmd *editCmd) identifyEditor(cfg *config.Config) (string, error) {
var editors []string
hupfdule marked this conversation as resolved.
Show resolved Hide resolved

// if an editor is specified as commandline argument, consider it
// as alternative
if cmd.editor != "" {
editors = append(editors, cmd.editor)
}

// if an editor is configured in the config.toml, consider it as
// alternative
if cfg.Edit.Editor != "" {
editors = append(editors, cfg.Edit.Editor)
}

// specifiy a fixed list of other alternatives
editors = append(editors, "$VISUAL", "nvim", "vim", "sensible-editor", "$EDITOR")
tyru marked this conversation as resolved.
Show resolved Hide resolved

for _, editor := range editors {
// resolve content of environment variables
var editorName string
if editor[0] == '$' {
editorName = os.Getenv(editor[1:])
} else {
editorName = editor
}

path, err := exec.LookPath(editorName)
if err != nil {
logger.Debug(editor + " not found in $PATH")
hupfdule marked this conversation as resolved.
Show resolved Hide resolved
} else if path != "" {
logger.Debug("Using " + path + " as editor")
return editorName, nil
}
}

return "", errors.New("No usable editor found")
}
3 changes: 3 additions & 0 deletions subcmd/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ Command
This is shortcut of:
volt profile rm -current {repository} [{repository2} ...]

edit [-e|--editor {editor}] {repository} [{repository2} ...]
Open the plugconf file(s) of one or more {repository} for editing.

profile set {name}
Set profile name

Expand Down