mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-15 21:37:09 +09:00
Fix autocomplete behavior for empty args
This also adds a modified version of go-shellwords as a dependency and removes the dependency on the original go-shellwords.
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -55,6 +55,3 @@
|
||||
[submodule "cmd/micro/vendor/github.com/flynn/json5"]
|
||||
path = cmd/micro/vendor/github.com/flynn/json5
|
||||
url = https://github.com/flynn/json5
|
||||
[submodule "cmd/micro/vendor/github.com/mattn/go-shellwords"]
|
||||
path = cmd/micro/vendor/github.com/mattn/go-shellwords
|
||||
url = https://github.com/mattn/go-shellwords
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/clipboard"
|
||||
"github.com/zyedidia/micro/cmd/micro/shellwords"
|
||||
"github.com/zyedidia/tcell"
|
||||
)
|
||||
|
||||
@@ -997,7 +998,7 @@ func (v *View) SaveAs(usePlugin bool) bool {
|
||||
filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
|
||||
if !canceled {
|
||||
// the filename might or might not be quoted, so unquote first then join the strings.
|
||||
args, err := SplitCommandArgs(filename)
|
||||
args, err := shellwords.Split(filename)
|
||||
filename = strings.Join(args, " ")
|
||||
if err != nil {
|
||||
messenger.Error("Error parsing arguments: ", err)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/zyedidia/micro/cmd/micro/shellwords"
|
||||
)
|
||||
|
||||
// A Command contains a action (a function to call) as well as information about how to autocomplete the command
|
||||
@@ -285,7 +286,7 @@ func Open(args []string) {
|
||||
if len(args) > 0 {
|
||||
filename := args[0]
|
||||
// the filename might or might not be quoted, so unquote first then join the strings.
|
||||
args, err := SplitCommandArgs(filename)
|
||||
args, err := shellwords.Split(filename)
|
||||
if err != nil {
|
||||
messenger.Error("Error parsing args ", err)
|
||||
return
|
||||
@@ -500,7 +501,7 @@ func Bind(args []string) {
|
||||
// Run runs a shell command in the background
|
||||
func Run(args []string) {
|
||||
// Run a shell command in the background (openTerm is false)
|
||||
HandleShellCommand(JoinCommandArgs(args...), false, true)
|
||||
HandleShellCommand(shellwords.Join(args...), false, true)
|
||||
}
|
||||
|
||||
// Quit closes the main view
|
||||
@@ -645,7 +646,7 @@ func ReplaceAll(args []string) {
|
||||
|
||||
// RunShellCommand executes a shell command and returns the output/error
|
||||
func RunShellCommand(input string) (string, error) {
|
||||
args, err := SplitCommandArgs(input)
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -665,7 +666,7 @@ func RunShellCommand(input string) (string, error) {
|
||||
// The openTerm argument specifies whether a terminal should be opened (for viewing output
|
||||
// or interacting with stdin)
|
||||
func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
|
||||
args, err := SplitCommandArgs(input)
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
@@ -735,7 +736,7 @@ func HandleShellCommand(input string, openTerm bool, waitToFinish bool) string {
|
||||
|
||||
// HandleCommand handles input from the user
|
||||
func HandleCommand(input string) {
|
||||
args, err := SplitCommandArgs(input)
|
||||
args, err := shellwords.Split(input)
|
||||
if err != nil {
|
||||
messenger.Error("Error parsing args ", err)
|
||||
return
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/zyedidia/clipboard"
|
||||
"github.com/zyedidia/micro/cmd/micro/shellwords"
|
||||
"github.com/zyedidia/tcell"
|
||||
)
|
||||
|
||||
@@ -272,7 +273,7 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
|
||||
response, canceled = m.response, false
|
||||
m.history[historyType][len(m.history[historyType])-1] = response
|
||||
case tcell.KeyTab:
|
||||
args, err := SplitCommandArgs(m.response)
|
||||
args, err := shellwords.Split(m.response)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@@ -322,7 +323,7 @@ func (m *Messenger) Prompt(prompt, placeholder, historyType string, completionTy
|
||||
}
|
||||
|
||||
if chosen != "" {
|
||||
m.response = JoinCommandArgs(append(args[:len(args)-1], chosen)...)
|
||||
m.response = shellwords.Join(append(args[:len(args)-1], chosen)...)
|
||||
m.cursorx = Count(m.response)
|
||||
}
|
||||
}
|
||||
|
||||
21
cmd/micro/shellwords/LICENSE
Normal file
21
cmd/micro/shellwords/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Yasuhiro Matsumoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
49
cmd/micro/shellwords/README.md
Normal file
49
cmd/micro/shellwords/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
This is a modified version of `go-shellwords` for the micro editor.
|
||||
|
||||
# go-shellwords
|
||||
|
||||
[](https://coveralls.io/r/mattn/go-shellwords?branch=master)
|
||||
[](https://travis-ci.org/mattn/go-shellwords)
|
||||
|
||||
Parse line as shell words.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
args, err := shellwords.Parse("./foo --bar=baz")
|
||||
// args should be ["./foo", "--bar=baz"]
|
||||
```
|
||||
|
||||
```go
|
||||
os.Setenv("FOO", "bar")
|
||||
p := shellwords.NewParser()
|
||||
p.ParseEnv = true
|
||||
args, err := p.Parse("./foo $FOO")
|
||||
// args should be ["./foo", "bar"]
|
||||
```
|
||||
|
||||
```go
|
||||
p := shellwords.NewParser()
|
||||
p.ParseBacktick = true
|
||||
args, err := p.Parse("./foo `echo $SHELL`")
|
||||
// args should be ["./foo", "/bin/bash"]
|
||||
```
|
||||
|
||||
```go
|
||||
shellwords.ParseBacktick = true
|
||||
p := shellwords.NewParser()
|
||||
args, err := p.Parse("./foo `echo $SHELL`")
|
||||
// args should be ["./foo", "/bin/bash"]
|
||||
```
|
||||
|
||||
# Thanks
|
||||
|
||||
This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine).
|
||||
|
||||
# License
|
||||
|
||||
under the MIT License: http://mattn.mit-license.org/2017
|
||||
|
||||
# Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
||||
189
cmd/micro/shellwords/shellwords.go
Normal file
189
cmd/micro/shellwords/shellwords.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var envRe = regexp.MustCompile(`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`)
|
||||
|
||||
func isSpace(r rune) bool {
|
||||
switch r {
|
||||
case ' ', '\t', '\r', '\n':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func replaceEnv(s string) string {
|
||||
return envRe.ReplaceAllStringFunc(s, func(s string) string {
|
||||
s = s[1:]
|
||||
if s[0] == '{' {
|
||||
s = s[1 : len(s)-1]
|
||||
}
|
||||
return os.Getenv(s)
|
||||
})
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
Position int
|
||||
}
|
||||
|
||||
func NewParser() *Parser {
|
||||
return &Parser{0}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(line string) ([]string, error) {
|
||||
args := []string{}
|
||||
buf := ""
|
||||
var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
|
||||
backtick := ""
|
||||
|
||||
pos := -1
|
||||
got := false
|
||||
|
||||
loop:
|
||||
for i, r := range line {
|
||||
if escaped {
|
||||
buf += string(r)
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
|
||||
if r == '\\' {
|
||||
if singleQuoted {
|
||||
buf += string(r)
|
||||
} else {
|
||||
escaped = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if isSpace(r) {
|
||||
if singleQuoted || doubleQuoted || backQuote || dollarQuote {
|
||||
buf += string(r)
|
||||
backtick += string(r)
|
||||
} else if got {
|
||||
buf = replaceEnv(buf)
|
||||
args = append(args, buf)
|
||||
buf = ""
|
||||
got = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '`':
|
||||
if !singleQuoted && !doubleQuoted && !dollarQuote {
|
||||
if backQuote {
|
||||
out, err := shellRun(backtick)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = out
|
||||
}
|
||||
backtick = ""
|
||||
backQuote = !backQuote
|
||||
continue
|
||||
backtick = ""
|
||||
backQuote = !backQuote
|
||||
}
|
||||
case ')':
|
||||
if !singleQuoted && !doubleQuoted && !backQuote {
|
||||
if dollarQuote {
|
||||
out, err := shellRun(backtick)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = out
|
||||
}
|
||||
backtick = ""
|
||||
dollarQuote = !dollarQuote
|
||||
continue
|
||||
backtick = ""
|
||||
dollarQuote = !dollarQuote
|
||||
}
|
||||
case '(':
|
||||
if !singleQuoted && !doubleQuoted && !backQuote {
|
||||
if !dollarQuote && len(buf) > 0 && buf == "$" {
|
||||
dollarQuote = true
|
||||
buf += "("
|
||||
continue
|
||||
} else {
|
||||
return nil, errors.New("invalid command line string")
|
||||
}
|
||||
}
|
||||
case '"':
|
||||
if !singleQuoted && !dollarQuote {
|
||||
doubleQuoted = !doubleQuoted
|
||||
continue
|
||||
}
|
||||
case '\'':
|
||||
if !doubleQuoted && !dollarQuote {
|
||||
singleQuoted = !singleQuoted
|
||||
continue
|
||||
}
|
||||
case ';', '&', '|', '<', '>':
|
||||
if !(escaped || singleQuoted || doubleQuoted || backQuote) {
|
||||
pos = i
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
got = true
|
||||
buf += string(r)
|
||||
if backQuote || dollarQuote {
|
||||
backtick += string(r)
|
||||
}
|
||||
}
|
||||
|
||||
buf = replaceEnv(buf)
|
||||
args = append(args, buf)
|
||||
|
||||
if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
|
||||
return nil, errors.New("invalid command line string")
|
||||
}
|
||||
|
||||
p.Position = pos
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func Split(line string) ([]string, error) {
|
||||
return NewParser().Parse(line)
|
||||
}
|
||||
|
||||
func Join(args ...string) string {
|
||||
var buf bytes.Buffer
|
||||
for i, w := range args {
|
||||
if i != 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
if w == "" {
|
||||
buf.WriteString("''")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, b := range w {
|
||||
switch b {
|
||||
case 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u',
|
||||
'v', 'w', 'x', 'y', 'z',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||||
'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||
'O', 'P', 'Q', 'R', 'S', 'T', 'U',
|
||||
'V', 'W', 'X', 'Y', 'Z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'_', '-', '.', ',', ':', '/', '@':
|
||||
buf.WriteString(string(b))
|
||||
default:
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteString(string(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
229
cmd/micro/shellwords/shellwords_test.go
Normal file
229
cmd/micro/shellwords/shellwords_test.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testcases = []struct {
|
||||
line string
|
||||
expected []string
|
||||
}{
|
||||
{`var --bar=baz`, []string{`var`, `--bar=baz`}},
|
||||
{`var --bar="baz"`, []string{`var`, `--bar=baz`}},
|
||||
{`var "--bar=baz"`, []string{`var`, `--bar=baz`}},
|
||||
{`var "--bar='baz'"`, []string{`var`, `--bar='baz'`}},
|
||||
{"var --bar=`baz`", []string{`var`, "--bar=`baz`"}},
|
||||
{`var "--bar=\"baz'"`, []string{`var`, `--bar="baz'`}},
|
||||
{`var "--bar=\'baz\'"`, []string{`var`, `--bar='baz'`}},
|
||||
{`var --bar='\'`, []string{`var`, `--bar=\`}},
|
||||
{`var "--bar baz"`, []string{`var`, `--bar baz`}},
|
||||
{`var --"bar baz"`, []string{`var`, `--bar baz`}},
|
||||
{`var --"bar baz"`, []string{`var`, `--bar baz`}},
|
||||
}
|
||||
|
||||
func TestSimple(t *testing.T) {
|
||||
for _, testcase := range testcases {
|
||||
args, err := Parse(testcase.line)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(args, testcase.expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", testcase.expected, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
_, err := Parse("foo '")
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = Parse(`foo "`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
|
||||
_, err = Parse("foo `")
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastSpace(t *testing.T) {
|
||||
args, err := Parse("foo bar\\ ")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(args) != 2 {
|
||||
t.Fatal("Should have two elements")
|
||||
}
|
||||
if args[0] != "foo" {
|
||||
t.Fatal("1st element should be `foo`")
|
||||
}
|
||||
if args[1] != "bar " {
|
||||
t.Fatal("1st element should be `bar `")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktick(t *testing.T) {
|
||||
goversion, err := shellRun("go version")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parser := NewParser()
|
||||
parser.ParseBacktick = true
|
||||
args, err := parser.Parse("echo `go version`")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", goversion}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
args, err = parser.Parse(`echo $(echo foo)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected = []string{"echo", "foo"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
parser.ParseBacktick = false
|
||||
args, err = parser.Parse(`echo $(echo "foo")`)
|
||||
expected = []string{"echo", `$(echo "foo")`}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
args, err = parser.Parse("echo $(`echo1)")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected = []string{"echo", "$(`echo1)"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBacktickError(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.ParseBacktick = true
|
||||
_, err := parser.Parse("echo `go Version`")
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
expected := "exit status 2:go: unknown subcommand \"Version\"\nRun 'go help' for usage.\n"
|
||||
if expected != err.Error() {
|
||||
t.Fatalf("Expected %q, but %q", expected, err.Error())
|
||||
}
|
||||
_, err = parser.Parse(`echo $(echo1)`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo $(echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo $ (echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo (echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
_, err = parser.Parse(`echo )echo1`)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
os.Setenv("FOO", "bar")
|
||||
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
args, err := parser.Parse("echo $FOO")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", "bar"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoEnv(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
args, err := parser.Parse("echo $BAR")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", ""}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDupEnv(t *testing.T) {
|
||||
os.Setenv("FOO", "bar")
|
||||
os.Setenv("FOO_BAR", "baz")
|
||||
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
args, err := parser.Parse("echo $$FOO$")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := []string{"echo", "$bar$"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
args, err = parser.Parse("echo $${FOO_BAR}$")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected = []string{"echo", "$baz$"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHaveMore(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser.ParseEnv = true
|
||||
|
||||
line := "echo foo; seq 1 10"
|
||||
args, err := parser.Parse(line)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
expected := []string{"echo", "foo"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
if parser.Position == 0 {
|
||||
t.Fatalf("Commands should be remaining")
|
||||
}
|
||||
|
||||
line = string([]rune(line)[parser.Position+1:])
|
||||
args, err = parser.Parse(line)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
expected = []string{"seq", "1", "10"}
|
||||
if !reflect.DeepEqual(args, expected) {
|
||||
t.Fatalf("Expected %#v, but %#v:", expected, args)
|
||||
}
|
||||
|
||||
if parser.Position > 0 {
|
||||
t.Fatalf("Commands should not be remaining")
|
||||
}
|
||||
}
|
||||
22
cmd/micro/shellwords/util_posix.go
Normal file
22
cmd/micro/shellwords/util_posix.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// +build !windows
|
||||
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func shellRun(line string) (string, error) {
|
||||
shell := os.Getenv("SHELL")
|
||||
b, err := exec.Command(shell, "-c", line).Output()
|
||||
if err != nil {
|
||||
if eerr, ok := err.(*exec.ExitError); ok {
|
||||
b = eerr.Stderr
|
||||
}
|
||||
return "", errors.New(err.Error() + ":" + string(b))
|
||||
}
|
||||
return strings.TrimSpace(string(b)), nil
|
||||
}
|
||||
20
cmd/micro/shellwords/util_windows.go
Normal file
20
cmd/micro/shellwords/util_windows.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package shellwords
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func shellRun(line string) (string, error) {
|
||||
shell := os.Getenv("COMSPEC")
|
||||
b, err := exec.Command(shell, "/c", line).Output()
|
||||
if err != nil {
|
||||
if eerr, ok := err.(*exec.ExitError); ok {
|
||||
b = eerr.Stderr
|
||||
}
|
||||
return "", errors.New(err.Error() + ":" + string(b))
|
||||
}
|
||||
return strings.TrimSpace(string(b)), nil
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/mattn/go-shellwords"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
@@ -277,55 +275,6 @@ func ShortFuncName(i interface{}) string {
|
||||
return strings.TrimPrefix(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name(), "main.(*View).")
|
||||
}
|
||||
|
||||
// SplitCommandArgs separates multiple command arguments which may be quoted.
|
||||
// The returned slice contains at least one string
|
||||
func SplitCommandArgs(input string) ([]string, error) {
|
||||
shellwords.ParseEnv = true
|
||||
shellwords.ParseBacktick = true
|
||||
return shellwords.Parse(input)
|
||||
}
|
||||
|
||||
// JoinCommandArgs joins multiple command arguments and quote the strings if needed.
|
||||
func JoinCommandArgs(args ...string) string {
|
||||
var buf bytes.Buffer
|
||||
for i, w := range args {
|
||||
if i != 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
if w == "" {
|
||||
buf.WriteString("''")
|
||||
continue
|
||||
}
|
||||
|
||||
strBytes := []byte(w)
|
||||
for _, b := range strBytes {
|
||||
switch b {
|
||||
case
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u',
|
||||
'v', 'w', 'x', 'y', 'z',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||||
'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||
'O', 'P', 'Q', 'R', 'S', 'T', 'U',
|
||||
'V', 'W', 'X', 'Y', 'Z',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'_', '-', '.', ',', ':', '/', '@':
|
||||
buf.WriteByte(b)
|
||||
case '\n':
|
||||
buf.WriteString("'\n'")
|
||||
default:
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(b)
|
||||
}
|
||||
}
|
||||
|
||||
// return buf.String()
|
||||
// buf.WriteString(w)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ReplaceHome takes a path as input and replaces ~ at the start of the path with the user's
|
||||
// home directory. Does nothing if the path does not start with '~'.
|
||||
func ReplaceHome(path string) string {
|
||||
|
||||
1
cmd/micro/vendor/github.com/mattn/go-shellwords
generated
vendored
1
cmd/micro/vendor/github.com/mattn/go-shellwords
generated
vendored
Submodule cmd/micro/vendor/github.com/mattn/go-shellwords deleted from 95c860c189
Reference in New Issue
Block a user