Skip to content

Commit 3afe097

Browse files
authored
Merge pull request #278 from hupfdule/subcmd_edit
Introduce new subcmd `edit`
2 parents b81395d + 502755c commit 3afe097

File tree

6 files changed

+265
-2
lines changed

6 files changed

+265
-2
lines changed

CMDREF.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ Command
3333
This is shortcut of:
3434
volt profile rm -current {repository} [{repository2} ...]
3535
36+
edit [-e|--editor {editor}] {repository} [{repository2} ...]
37+
Open the plugconf file(s) of one or more {repository} for editing.
38+
3639
profile set {name}
3740
Set profile name
3841
@@ -112,6 +115,23 @@ Description
112115
volt profile rm {current profile} {repository} [{repository2} ...]
113116
```
114117

118+
# volt edit
119+
120+
```
121+
Usage
122+
volt edit [-help] [-e|--editor {editor}] {repository} [{repository2} ...]
123+
124+
Quick example
125+
$ volt edit tyru/caw.vim # will open the plugconf file for tyru/caw.vim for editing
126+
127+
Description
128+
Open the plugconf file(s) of one or more {repository} for editing.
129+
130+
If the -e option was given, use the given editor for editing those files (unless it cannot be found)
131+
132+
It also calls "volt build" afterwards if modifications were made to the plugconf file(s).
133+
```
134+
115135
# volt enable
116136

117137
```

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,13 @@ create_skeleton_plugconf = true
181181
# installed, it tries to execute "git clone" or "git pull" as a fallback
182182
# * false: "volt get" or "volt get -u" won't try to execute fallback commands
183183
fallback_git_cmd = true
184+
185+
[edit]
186+
# If you ever wanted to use emacs to edit your vim plugin config, you can
187+
# do so with the following. If not specified, volt will try to use
188+
# vim/nvim, $VISUAL, sensible-editor, or $EDITOR in this order until a usable
189+
# one is found.
190+
editor = "emacs"
184191
```
185192

186193
## Features

_contrib/completion/bash

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ this_plug() {
2626
volt list -f "{{ range .Profiles }}{{ if eq \"$1\" .Name }}{{ range .ReposPath }}{{ println . }}{{ end }}{{ end }}{{ end }}" | sed -E 's@^(www\.)?github\.com/@@' | sort -u
2727
}
2828

29-
CMDS="get rm list enable disable profile build migrate self-upgrade version"
29+
CMDS="get rm list enable disable edit profile build migrate self-upgrade version"
3030
PROFILE_CMDS="set show list new destroy rename add rm"
3131
MIGRATE_CMDS="lockjson plugconf/config-func"
3232

@@ -53,7 +53,7 @@ _volt() {
5353
elif [[ "${first}" == "profile" && "${last}" == "profile" ]] ; then
5454
COMPREPLY=( $(compgen -W "${PROFILE_CMDS}" -- ${cur}) )
5555

56-
elif [[ "${first}" =~ ^(rm|disable)$ ]] ; then
56+
elif [[ "${first}" =~ ^(rm|disable|edit)$ ]] ; then
5757
local profile=$(get_profile)
5858
plugs=$(get_plugs "$profile" "this")
5959
COMPREPLY=( $(compgen -W "${plugs}" -- ${cur}) )

config/config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type Config struct {
1212
Alias map[string][]string `toml:"alias"`
1313
Build configBuild `toml:"build"`
1414
Get configGet `toml:"get"`
15+
Edit configEdit `toml:"edit"`
1516
}
1617

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

29+
// configEdit is a config for 'volt edit'.
30+
type configEdit struct {
31+
Editor string `toml:"editor"`
32+
}
33+
2834
const (
2935
// SymlinkBuilder creates symlinks when 'volt build'.
3036
SymlinkBuilder = "symlink"
@@ -43,6 +49,9 @@ func initialConfigTOML() *Config {
4349
CreateSkeletonPlugconf: &trueValue,
4450
FallbackGitCmd: &falseValue,
4551
},
52+
Edit: configEdit{
53+
Editor: "",
54+
},
4655
}
4756
}
4857

@@ -76,6 +85,9 @@ func merge(cfg, initCfg *Config) {
7685
if cfg.Get.FallbackGitCmd == nil {
7786
cfg.Get.FallbackGitCmd = initCfg.Get.FallbackGitCmd
7887
}
88+
if cfg.Edit.Editor == "" {
89+
cfg.Edit.Editor = initCfg.Edit.Editor
90+
}
7991
}
8092

8193
func validate(cfg *Config) error {

subcmd/edit.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
package subcmd
2+
3+
import (
4+
"errors"
5+
"flag"
6+
"fmt"
7+
"os"
8+
"os/exec"
9+
10+
"github.com/vim-volt/volt/config"
11+
"github.com/vim-volt/volt/lockjson"
12+
"github.com/vim-volt/volt/logger"
13+
"github.com/vim-volt/volt/pathutil"
14+
"github.com/vim-volt/volt/subcmd/builder"
15+
)
16+
17+
func init() {
18+
cmdMap["edit"] = &editCmd{}
19+
}
20+
21+
type editCmd struct {
22+
helped bool
23+
editor string
24+
}
25+
26+
func (cmd *editCmd) ProhibitRootExecution(args []string) bool { return true }
27+
28+
func (cmd *editCmd) FlagSet() *flag.FlagSet {
29+
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
30+
fs.SetOutput(os.Stdout)
31+
fs.Usage = func() {
32+
fmt.Print(`
33+
Usage
34+
volt edit [-help] [-e|--editor {editor}] {repository} [{repository2} ...]
35+
36+
Quick example
37+
$ volt edit tyru/caw.vim # will open the plugconf file for tyru/caw.vim for editing
38+
39+
Description
40+
Open the plugconf file(s) of one or more {repository} for editing.
41+
42+
If the -e option was given, use the given editor for editing those files (unless it cannot be found)
43+
44+
It also calls "volt build" afterwards if modifications were made to the plugconf file(s).` + "\n\n")
45+
//fmt.Println("Options")
46+
//fs.PrintDefaults()
47+
fmt.Println()
48+
cmd.helped = true
49+
}
50+
fs.StringVar(&cmd.editor, "editor", "", "Use the given editor for editing the plugconf files")
51+
fs.StringVar(&cmd.editor, "e", "", "Use the given editor for editing the plugconf files")
52+
return fs
53+
}
54+
55+
func (cmd *editCmd) Run(args []string) *Error {
56+
reposPathList, err := cmd.parseArgs(args)
57+
if err == ErrShowedHelp {
58+
return nil
59+
}
60+
if err != nil {
61+
return &Error{Code: 10, Msg: "Failed to parse args: " + err.Error()}
62+
}
63+
64+
hasChanges, err := cmd.doEdit(reposPathList)
65+
if err != nil {
66+
return &Error{Code: 15, Msg: "Failed to edit plugconf file: " + err.Error()}
67+
}
68+
69+
// Build opt dir
70+
if hasChanges {
71+
err = builder.Build(false)
72+
if err != nil {
73+
return &Error{Code: 12, Msg: "Could not build " + pathutil.VimVoltDir() + ": " + err.Error()}
74+
}
75+
}
76+
77+
return nil
78+
}
79+
80+
func (cmd *editCmd) doEdit(reposPathList []pathutil.ReposPath) (bool, error) {
81+
// Read lock.json
82+
lockJSON, err := lockjson.Read()
83+
if err != nil {
84+
return false, err
85+
}
86+
87+
// Read config.toml
88+
cfg, err := config.Read()
89+
if err != nil {
90+
return false, errors.New("could not read config.toml: " + err.Error())
91+
}
92+
93+
editor, err := cmd.identifyEditor(cfg)
94+
if err != nil || editor == "" {
95+
return false, &Error{Code: 30, Msg: "No usable editor found"}
96+
}
97+
98+
changeWasMade := false
99+
for _, reposPath := range reposPathList {
100+
101+
// Edit plugconf file
102+
plugconfPath := reposPath.Plugconf()
103+
104+
// Install a new template if none exists
105+
if !pathutil.Exists(plugconfPath) {
106+
getCmd := new(getCmd)
107+
logger.Debugf("Installing new plugconf for '%s'.", reposPath)
108+
getCmd.downloadPlugconf(reposPath)
109+
}
110+
111+
// Remember modification time before opening the editor
112+
info, err := os.Stat(plugconfPath)
113+
if err != nil {
114+
return false, err
115+
}
116+
mTimeBefore := info.ModTime()
117+
118+
// Call the editor with the plugconf file
119+
editorCmd := exec.Command(editor, plugconfPath)
120+
editorCmd.Stdin = os.Stdin
121+
editorCmd.Stdout = os.Stdout
122+
if err = editorCmd.Run(); err != nil {
123+
logger.Error("Error calling editor for '%s': %s", reposPath, err.Error)
124+
continue
125+
}
126+
127+
// Get modification time after closing the editor
128+
info, err = os.Stat(plugconfPath)
129+
if err != nil {
130+
return false, err
131+
}
132+
mTimeAfter := info.ModTime()
133+
134+
// A change was made if the modification time was updated
135+
changeWasMade = changeWasMade || mTimeAfter.After(mTimeBefore)
136+
137+
// Remove repository from lock.json
138+
err = lockJSON.Repos.RemoveAllReposPath(reposPath)
139+
err2 := lockJSON.Profiles.RemoveAllReposPath(reposPath)
140+
if err == nil || err2 == nil {
141+
// ignore?
142+
}
143+
}
144+
145+
// Write to lock.json
146+
if err = lockJSON.Write(); err != nil {
147+
return changeWasMade, err
148+
}
149+
return changeWasMade, nil
150+
}
151+
152+
func (cmd *editCmd) parseArgs(args []string) (pathutil.ReposPathList, error) {
153+
fs := cmd.FlagSet()
154+
fs.Parse(args)
155+
if cmd.helped {
156+
return nil, ErrShowedHelp
157+
}
158+
159+
if len(fs.Args()) == 0 {
160+
fs.Usage()
161+
return nil, errors.New("repository was not given")
162+
}
163+
164+
// Normalize repos path
165+
reposPathList := make(pathutil.ReposPathList, 0, len(fs.Args()))
166+
for _, arg := range fs.Args() {
167+
reposPath, err := pathutil.NormalizeRepos(arg)
168+
if err != nil {
169+
return nil, err
170+
}
171+
reposPathList = append(reposPathList, reposPath)
172+
}
173+
174+
return reposPathList, nil
175+
}
176+
177+
func (cmd *editCmd) identifyEditor(cfg *config.Config) (string, error) {
178+
editors := make([]string, 0, 6)
179+
180+
// if an editor is specified as commandline argument, consider it
181+
// as alternative
182+
if cmd.editor != "" {
183+
editors = append(editors, cmd.editor)
184+
}
185+
186+
// if an editor is configured in the config.toml, consider it as
187+
// alternative
188+
if cfg.Edit.Editor != "" {
189+
editors = append(editors, cfg.Edit.Editor)
190+
}
191+
192+
vimExecutable, err := pathutil.VimExecutable()
193+
if err != nil {
194+
logger.Debug("No vim executable found in $PATH")
195+
} else {
196+
editors = append(editors, vimExecutable)
197+
}
198+
199+
// specifiy a fixed list of other alternatives
200+
editors = append(editors, "$VISUAL", "sensible-editor", "$EDITOR")
201+
202+
for _, editor := range editors {
203+
// resolve content of environment variables
204+
var editorName string
205+
if editor[0] == '$' {
206+
editorName = os.Getenv(editor[1:])
207+
} else {
208+
editorName = editor
209+
}
210+
211+
path, err := exec.LookPath(editorName)
212+
if err != nil {
213+
logger.Debug(editorName + " not found in $PATH")
214+
} else if path != "" {
215+
logger.Debug("Using " + path + " as editor")
216+
return editorName, nil
217+
}
218+
}
219+
220+
return "", errors.New("No usable editor found")
221+
}

subcmd/help.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ Command
5858
This is shortcut of:
5959
volt profile rm -current {repository} [{repository2} ...]
6060
61+
edit [-e|--editor {editor}] {repository} [{repository2} ...]
62+
Open the plugconf file(s) of one or more {repository} for editing.
63+
6164
profile set {name}
6265
Set profile name
6366

0 commit comments

Comments
 (0)