mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-10-21 04:32:05 +00:00
Merge branch 'go-shlex-empty-quoted-strings' of https://github.com/anandghegde/kitty
This commit is contained in:
commit
784340ae37
2 changed files with 25 additions and 9 deletions
|
|
@ -23,8 +23,8 @@ import (
|
|||
)
|
||||
|
||||
type Word struct {
|
||||
Value string // The word is empty if EOF is reached
|
||||
Pos int // The position in the input string of the word or the trailer
|
||||
Value string // The word, which can be empty if it was an empty quoted string
|
||||
Pos int // The position in the input string of the word or the trailer, -1 if EOF is reached
|
||||
Err error // Indicates an error (unterminated string or trailing unescaped backslash)
|
||||
Trailer string // Extra trailing data such as an unterminated string or an unescaped backslash. Present only if Err != nil
|
||||
}
|
||||
|
|
@ -45,6 +45,7 @@ type Lexer struct {
|
|||
src string
|
||||
src_sz, src_pos, word_start int
|
||||
buf strings.Builder
|
||||
allow_empty bool
|
||||
}
|
||||
|
||||
// NewLexer creates a new lexer from an input string.
|
||||
|
|
@ -58,6 +59,7 @@ func (self *Lexer) start_word() {
|
|||
}
|
||||
|
||||
func (self *Lexer) get_word() Word {
|
||||
self.allow_empty = false
|
||||
return Word{Pos: self.word_start, Value: self.buf.String()}
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +79,7 @@ func (self *Lexer) write_escaped_ch() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Next returns the next word. At EOF Word.Value will be ""
|
||||
// Next returns the next word. At EOF Word.Pos will be -1
|
||||
func (self *Lexer) Next() (ans Word) {
|
||||
const string_with_escapes_delim = '"'
|
||||
const string_without_escapes_delim = '\''
|
||||
|
|
@ -113,7 +115,7 @@ func (self *Lexer) Next() (ans Word) {
|
|||
switch ch {
|
||||
case ' ', '\n', '\r', '\t':
|
||||
self.state = lex_normal
|
||||
if self.buf.Len() > 0 {
|
||||
if self.buf.Len() > 0 || self.allow_empty {
|
||||
return self.get_word()
|
||||
}
|
||||
case string_with_escapes_delim:
|
||||
|
|
@ -134,6 +136,7 @@ func (self *Lexer) Next() (ans Word) {
|
|||
switch ch {
|
||||
case string_without_escapes_delim:
|
||||
self.state = word
|
||||
self.allow_empty = true
|
||||
default:
|
||||
self.write_ch(ch)
|
||||
}
|
||||
|
|
@ -141,6 +144,7 @@ func (self *Lexer) Next() (ans Word) {
|
|||
switch ch {
|
||||
case string_with_escapes_delim:
|
||||
self.state = word
|
||||
self.allow_empty = true
|
||||
case escape_char:
|
||||
self.write_escaped_ch()
|
||||
default:
|
||||
|
|
@ -151,7 +155,7 @@ func (self *Lexer) Next() (ans Word) {
|
|||
switch self.state {
|
||||
case word:
|
||||
self.state = lex_normal
|
||||
if self.buf.Len() > 0 {
|
||||
if self.buf.Len() > 0 || self.allow_empty {
|
||||
return self.get_word()
|
||||
}
|
||||
case string_with_escapes, string_without_escapes:
|
||||
|
|
@ -163,6 +167,7 @@ func (self *Lexer) Next() (ans Word) {
|
|||
case lex_normal:
|
||||
|
||||
}
|
||||
ans.Pos = -1
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +180,7 @@ func Split(s string) (ans []string, err error) {
|
|||
if word.Err != nil {
|
||||
return ans, word.Err
|
||||
}
|
||||
if word.Value == "" {
|
||||
if word.Pos < 0 {
|
||||
break
|
||||
}
|
||||
ans = append(ans, word.Value)
|
||||
|
|
@ -200,7 +205,7 @@ func SplitForCompletion(s string) (argv []string, position_of_last_arg int) {
|
|||
argv = make([]string, 0, len(s)/4)
|
||||
for {
|
||||
word := t.Next()
|
||||
if word.Value == "" {
|
||||
if word.Pos < 0 || word.Err != nil {
|
||||
if word.Trailer == "" {
|
||||
trimmed := strings.TrimRight(s, " ")
|
||||
if len(trimmed) < len(s) { // trailing spaces
|
||||
|
|
@ -210,7 +215,7 @@ func SplitForCompletion(s string) (argv []string, position_of_last_arg int) {
|
|||
}
|
||||
if pos < len(s) { // trailing whitespace
|
||||
argv = append(argv, "")
|
||||
position_of_last_arg += len(s) - pos + 1
|
||||
position_of_last_arg = len(s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func TestSplit(t *testing.T) {
|
|||
if w.Err != nil {
|
||||
t.Fatal(w.Err)
|
||||
}
|
||||
if w.Value == "" {
|
||||
if w.Pos < 0 {
|
||||
break
|
||||
}
|
||||
ans = append(ans, Tok{w.Pos, w.Value})
|
||||
|
|
@ -68,6 +68,12 @@ func TestSplit(t *testing.T) {
|
|||
return
|
||||
}
|
||||
for q, expected := range map[string][]Tok{
|
||||
`a""`: {{0, "a"}},
|
||||
`a""b`: {{0, "ab"}},
|
||||
`-1 "" 2`: {{0, "-1"}, {3, ""}, {6, "2"}},
|
||||
`-1 '' 2`: {{0, "-1"}, {3, ""}, {6, "2"}},
|
||||
`a ""`: {{0, "a"}, {2, ""}},
|
||||
`""`: {{0, ""}},
|
||||
`"ab"`: {{0, "ab"}},
|
||||
`x "ab"y \m`: {{0, `x`}, {2, `aby`}, {8, `m`}},
|
||||
`x'y"\z'1`: {{0, `xy"\z1`}},
|
||||
|
|
@ -96,8 +102,13 @@ func TestSplitForCompletion(t *testing.T) {
|
|||
test("a b", 2, "a", "b")
|
||||
test("a b ", 4, "a", "b", "")
|
||||
test("a b ", 5, "a", "b", "")
|
||||
test("ab cd ", 6, "ab", "cd", "")
|
||||
test("launch --type ", 14, "launch", "--type", "")
|
||||
test(`a "b c" `, 8, "a", "b c", "")
|
||||
test(`a "b c"`, 2, "a", "b c")
|
||||
test(`a "b c`, 2, "a", "b c")
|
||||
test(`a "" b`, 5, "a", "", "b")
|
||||
test(`a '' `, 5, "a", "", "")
|
||||
}
|
||||
|
||||
func TestExpandANSICEscapes(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue