mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-03-22 15:11:24 +01:00
checkpoint -- extension
This commit is contained in:
parent
8729d1f491
commit
75f662a188
@ -1,12 +1,13 @@
|
||||
package shparse
|
||||
|
||||
const (
|
||||
CompTypeCommand = "command"
|
||||
CompTypeArg = "command-arg"
|
||||
CompTypeInvalid = "invalid"
|
||||
CompTypeVar = "var"
|
||||
CompTypeAssignment = "assignment"
|
||||
CompTypeBasic = "basic"
|
||||
CompTypeCommandMeta = "command-meta"
|
||||
CompTypeCommand = "command"
|
||||
CompTypeArg = "command-arg"
|
||||
CompTypeInvalid = "invalid"
|
||||
CompTypeVar = "var"
|
||||
CompTypeAssignment = "assignment"
|
||||
CompTypeBasic = "basic"
|
||||
)
|
||||
|
||||
type CompletionPos struct {
|
||||
@ -22,7 +23,6 @@ type CompletionPos struct {
|
||||
CmdWordPos int
|
||||
CompWord *WordType // set to the word we are completing (nil if we are starting a new word)
|
||||
CompWordOffset int // offset into compword (only if CmdWord is not nil)
|
||||
|
||||
}
|
||||
|
||||
func compTypeFromPos(cmdWordPos int) string {
|
||||
@ -97,10 +97,10 @@ func (cmd *CmdType) findCompletionPos_simple(pos int, superOffset int) Completio
|
||||
return rtn
|
||||
}
|
||||
|
||||
func (cmd *CmdType) findCompletionWordAtPos_none(pos int, superOffset int) CompletionPos {
|
||||
func (cmd *CmdType) findCompletionPos_none(pos int, superOffset int) CompletionPos {
|
||||
rtn := CompletionPos{RawPos: pos, SuperOffset: superOffset}
|
||||
if cmd.Type != CmdTypeNone {
|
||||
panic("findCompletionWordAtPos_none only works for CmdTypeNone")
|
||||
panic("findCompletionPos_none only works for CmdTypeNone")
|
||||
}
|
||||
var foundWord *WordType
|
||||
for _, word := range cmd.Words {
|
||||
@ -123,25 +123,31 @@ func (cmd *CmdType) findCompletionWordAtPos_none(pos int, superOffset int) Compl
|
||||
rtn.CompType = CompTypeBasic
|
||||
return rtn
|
||||
}
|
||||
foundWordOffset := pos - foundWord.Offset
|
||||
rtn.CompWord = foundWord
|
||||
rtn.CompWordOffset = pos - foundWord.Offset
|
||||
rtn.CompWordOffset = foundWordOffset
|
||||
if foundWord.uncompletable() {
|
||||
// ok, we're inside of a word in CmdTypeNone. if we're in an uncompletable word, return CompInvalid
|
||||
rtn.CompType = CompTypeInvalid
|
||||
return rtn
|
||||
}
|
||||
if foundWordOffset > 0 && foundWordOffset < foundWord.contentStartPos() {
|
||||
// cursor is in a weird position, between characters of a multi-char prefix (e.g. "$[*]{hello}" or $[*]'hello'). cannot complete.
|
||||
rtn.CompType = CompTypeInvalid
|
||||
return rtn
|
||||
}
|
||||
// revert to file completion
|
||||
rtn.CompType = CompTypeBasic
|
||||
return rtn
|
||||
}
|
||||
|
||||
func findCompletionWordAtPos(words []*WordType, pos int) *WordType {
|
||||
// WordTypeSimpleVar is special, if cursor is at the end of SimpleVar it is returned
|
||||
func findCompletionWordAtPos(words []*WordType, pos int, allowEndMatch bool) *WordType {
|
||||
// WordTypeSimpleVar is special (always allowEndMatch), if cursor is at the end of SimpleVar it is returned
|
||||
for _, word := range words {
|
||||
if pos > word.Offset && pos < word.Offset+len(word.Raw) {
|
||||
return word
|
||||
}
|
||||
if word.Type == WordTypeSimpleVar && pos == word.Offset+len(word.Raw) {
|
||||
if (allowEndMatch || word.Type == WordTypeSimpleVar) && pos == word.Offset+len(word.Raw) {
|
||||
return word
|
||||
}
|
||||
}
|
||||
@ -159,7 +165,7 @@ func findCompletionPosInWord(word *WordType, pos int, superOffset int) *Completi
|
||||
if pos > word.contentEndPos() {
|
||||
return nil
|
||||
}
|
||||
subWord := findCompletionWordAtPos(word.Subs, pos-word.contentStartPos())
|
||||
subWord := findCompletionWordAtPos(word.Subs, pos-word.contentStartPos(), false)
|
||||
if subWord == nil {
|
||||
return nil
|
||||
}
|
||||
@ -218,7 +224,7 @@ func findCompletionPosCmds(cmds []*CmdType, pos int, superOffset int) Completion
|
||||
rtn.CompType = CompTypeCommand
|
||||
return rtn
|
||||
}
|
||||
return cmd.findCompletionWordAtPos_none(pos, superOffset)
|
||||
return cmd.findCompletionPos_none(pos, superOffset)
|
||||
}
|
||||
}
|
||||
// past the end
|
||||
|
@ -93,35 +93,108 @@ func (ec *extendContext) appendWord(w *WordType) {
|
||||
|
||||
func (ec *extendContext) ensureCurWord() {
|
||||
if ec.CurWord == nil || ec.CurWord.Type != ec.Intention {
|
||||
ec.CurWord = MakeEmptyWord(ec.Intention, ec.QC, 0)
|
||||
ec.CurWord = MakeEmptyWord(ec.Intention, ec.QC, 0, true)
|
||||
ec.Rtn = append(ec.Rtn, ec.CurWord)
|
||||
}
|
||||
}
|
||||
|
||||
// grp, dq, ddq
|
||||
func extendWithSubs(buf *bytes.Buffer, word *WordType, wordPos int, extStr string) {
|
||||
|
||||
}
|
||||
|
||||
// lit, svar, varb, sq, dsq
|
||||
func extendLeafCh(buf *bytes.Buffer, wordOpen *bool, wtype string, qc QuoteContext, ch rune) {
|
||||
switch wtype {
|
||||
case WordTypeSimpleVar, WordTypeVarBrace:
|
||||
extendVar(buf, ch)
|
||||
|
||||
case WordTypeLit:
|
||||
if qc.cur() == WordTypeDQ {
|
||||
extendDQLit(buf, wordOpen, ch)
|
||||
} else {
|
||||
extendLit(buf, ch)
|
||||
}
|
||||
|
||||
case WordTypeSQ:
|
||||
extendSQ(buf, wordOpen, ch)
|
||||
|
||||
case WordTypeDSQ:
|
||||
extendDSQ(buf, wordOpen, ch)
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// lit, svar, varb sq, dsq
|
||||
func extendLeaf(buf *bytes.Buffer, wordOpen *bool, word *WordType, wordPos int, extStr string) {
|
||||
for _, ch := range extStr {
|
||||
extendLeafCh(buf, wordOpen, word.Type, word.QC, ch)
|
||||
}
|
||||
}
|
||||
|
||||
// lit, grp, svar, dq, ddq, varb, sq, dsq
|
||||
func Extend(word *WordType, wordPos int, extStr string, complete bool) utilfn.StrWithPos {
|
||||
if extStr == "" {
|
||||
return utilfn.StrWithPos{Str: string(word.Raw), Pos: wordPos}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
isEOW := wordPos >= word.contentEndPos()
|
||||
if isEOW {
|
||||
wordPos = word.contentEndPos()
|
||||
}
|
||||
if wordPos > 0 && wordPos < word.contentStartPos() {
|
||||
wordPos = word.contentStartPos()
|
||||
}
|
||||
wordOpen := false
|
||||
if wordPos >= word.contentStartPos() {
|
||||
wordOpen = true
|
||||
}
|
||||
buf.WriteString(string(word.Raw[0:wordPos])) // write the prefix
|
||||
if word.canHaveSubs() {
|
||||
extendWithSubs(&buf, word, wordPos, extStr)
|
||||
} else {
|
||||
extendLeaf(&buf, &wordOpen, word, wordPos, extStr)
|
||||
}
|
||||
if isEOW {
|
||||
// end-of-word, write the suffix (and optional ' '). return the end of the string
|
||||
wmeta := wordMetaMap[word.Type]
|
||||
buf.WriteString(wmeta.getSuffix())
|
||||
var rtnPos int
|
||||
if complete {
|
||||
buf.WriteRune(' ')
|
||||
rtnPos = utf8.RuneCount(buf.Bytes())
|
||||
} else {
|
||||
rtnPos = utf8.RuneCount(buf.Bytes()) - wmeta.SuffixLen
|
||||
}
|
||||
return utilfn.StrWithPos{Str: buf.String(), Pos: rtnPos}
|
||||
}
|
||||
// completion in the middle of a word (no ' ')
|
||||
rtnPos := utf8.RuneCount(buf.Bytes())
|
||||
buf.WriteString(string(word.Raw[wordPos:])) // write the suffix
|
||||
return utilfn.StrWithPos{Str: buf.String(), Pos: rtnPos}
|
||||
}
|
||||
|
||||
func (ec *extendContext) extend(ch rune) {
|
||||
if ch == 0 {
|
||||
return
|
||||
}
|
||||
switch ec.Intention {
|
||||
return
|
||||
}
|
||||
|
||||
case WordTypeSimpleVar, WordTypeVarBrace:
|
||||
ec.extendVar(ch)
|
||||
func isVarNameChar(ch rune) bool {
|
||||
return ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
|
||||
}
|
||||
|
||||
case WordTypeDQ, WordTypeDDQ:
|
||||
ec.extendDQ(ch)
|
||||
|
||||
case WordTypeSQ:
|
||||
ec.extendSQ(ch)
|
||||
|
||||
case WordTypeDSQ:
|
||||
ec.extendDSQ(ch)
|
||||
|
||||
case WordTypeLit:
|
||||
ec.extendLit(ch)
|
||||
|
||||
default:
|
||||
func extendVar(buf *bytes.Buffer, ch rune) {
|
||||
if ch == 0 {
|
||||
return
|
||||
}
|
||||
if !isVarNameChar(ch) {
|
||||
return
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
func getSpecialEscape(ch rune) string {
|
||||
@ -131,122 +204,110 @@ func getSpecialEscape(ch rune) string {
|
||||
return specialEsc[byte(ch)]
|
||||
}
|
||||
|
||||
func isVarNameChar(ch rune) bool {
|
||||
return ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')
|
||||
func writeSpecial(buf *bytes.Buffer, ch rune) {
|
||||
sesc := getSpecialEscape(ch)
|
||||
if sesc != "" {
|
||||
buf.WriteString(sesc)
|
||||
} else {
|
||||
utf8Lit := getUtf8Literal(ch)
|
||||
buf.WriteString(utf8Lit)
|
||||
}
|
||||
}
|
||||
|
||||
func (ec *extendContext) extendVar(ch rune) {
|
||||
if ch == 0 {
|
||||
return
|
||||
}
|
||||
if !isVarNameChar(ch) {
|
||||
return
|
||||
}
|
||||
ec.ensureCurWord()
|
||||
ec.CurWord.writeRune(ch)
|
||||
}
|
||||
|
||||
func (ec *extendContext) extendLit(ch rune) {
|
||||
func extendLit(buf *bytes.Buffer, ch rune) {
|
||||
if ch == 0 {
|
||||
return
|
||||
}
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) {
|
||||
dsqWord := MakeEmptyWord(WordTypeDSQ, ec.QC, 0)
|
||||
ec.appendWord(dsqWord)
|
||||
sesc := getSpecialEscape(ch)
|
||||
if sesc != "" {
|
||||
dsqWord.writeString(sesc)
|
||||
return
|
||||
} else {
|
||||
utf8Lit := getUtf8Literal(ch)
|
||||
dsqWord.writeString(utf8Lit)
|
||||
}
|
||||
writeSpecial(buf, ch)
|
||||
return
|
||||
}
|
||||
var bch = byte(ch)
|
||||
ec.ensureCurWord()
|
||||
if noEscChars[bch] {
|
||||
ec.CurWord.writeRune(ch)
|
||||
buf.WriteRune(ch)
|
||||
return
|
||||
}
|
||||
ec.CurWord.writeRune('\\')
|
||||
ec.CurWord.writeRune(ch)
|
||||
buf.WriteRune('\\')
|
||||
buf.WriteRune(ch)
|
||||
return
|
||||
}
|
||||
|
||||
func (ec *extendContext) extendDSQ(ch rune) {
|
||||
func extendDSQ(buf *bytes.Buffer, wordOpen *bool, ch rune) {
|
||||
if ch == 0 {
|
||||
return
|
||||
}
|
||||
ec.ensureCurWord()
|
||||
if ch == '\'' {
|
||||
ec.CurWord.writeRune('\\')
|
||||
ec.CurWord.writeRune(ch)
|
||||
return
|
||||
}
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) {
|
||||
sesc := getSpecialEscape(ch)
|
||||
if sesc != "" {
|
||||
ec.CurWord.writeString(sesc)
|
||||
} else {
|
||||
utf8Lit := getUtf8Literal(ch)
|
||||
ec.CurWord.writeString(utf8Lit)
|
||||
if *wordOpen {
|
||||
buf.WriteRune('\'')
|
||||
*wordOpen = false
|
||||
}
|
||||
writeSpecial(buf, ch)
|
||||
return
|
||||
}
|
||||
ec.CurWord.writeRune(ch)
|
||||
if *wordOpen {
|
||||
buf.WriteRune('$')
|
||||
buf.WriteRune('\'')
|
||||
*wordOpen = true
|
||||
}
|
||||
if ch == '\'' {
|
||||
buf.WriteRune('\\')
|
||||
buf.WriteRune(ch)
|
||||
return
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
return
|
||||
}
|
||||
|
||||
func (ec *extendContext) extendSQ(ch rune) {
|
||||
func extendSQ(buf *bytes.Buffer, wordOpen *bool, ch rune) {
|
||||
if ch == 0 {
|
||||
return
|
||||
}
|
||||
if ch == '\'' {
|
||||
litWord := &WordType{Type: WordTypeLit, QC: ec.QC}
|
||||
litWord.Raw = []rune{'\\', '\''}
|
||||
ec.appendWord(litWord)
|
||||
if *wordOpen {
|
||||
buf.WriteRune('\'')
|
||||
*wordOpen = false
|
||||
}
|
||||
buf.WriteRune('\\')
|
||||
buf.WriteRune('\'')
|
||||
return
|
||||
}
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) {
|
||||
dsqWord := MakeEmptyWord(WordTypeDSQ, ec.QC, 0)
|
||||
ec.appendWord(dsqWord)
|
||||
sesc := getSpecialEscape(ch)
|
||||
if sesc != "" {
|
||||
dsqWord.writeString(sesc)
|
||||
} else {
|
||||
utf8Lit := getUtf8Literal(ch)
|
||||
dsqWord.writeString(utf8Lit)
|
||||
if *wordOpen {
|
||||
buf.WriteRune('\'')
|
||||
*wordOpen = false
|
||||
}
|
||||
writeSpecial(buf, ch)
|
||||
return
|
||||
}
|
||||
ec.ensureCurWord()
|
||||
ec.CurWord.writeRune(ch)
|
||||
if !*wordOpen {
|
||||
buf.WriteRune('\'')
|
||||
*wordOpen = true
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
return
|
||||
}
|
||||
|
||||
func (ec *extendContext) extendDQ(ch rune) {
|
||||
func extendDQLit(buf *bytes.Buffer, wordOpen *bool, ch rune) {
|
||||
if ch == 0 {
|
||||
return
|
||||
}
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) {
|
||||
if *wordOpen {
|
||||
buf.WriteRune('"')
|
||||
*wordOpen = false
|
||||
}
|
||||
writeSpecial(buf, ch)
|
||||
return
|
||||
}
|
||||
if !*wordOpen {
|
||||
buf.WriteRune('"')
|
||||
*wordOpen = true
|
||||
}
|
||||
if ch == '"' || ch == '\\' || ch == '$' || ch == '`' {
|
||||
ec.ensureCurWord()
|
||||
ec.CurWord.writeRune('\\')
|
||||
ec.CurWord.writeRune(ch)
|
||||
buf.WriteRune('\\')
|
||||
buf.WriteRune(ch)
|
||||
return
|
||||
}
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) {
|
||||
dsqWord := MakeEmptyWord(WordTypeDSQ, ec.QC, 0)
|
||||
ec.appendWord(dsqWord)
|
||||
sesc := getSpecialEscape(ch)
|
||||
if sesc != "" {
|
||||
dsqWord.writeString(sesc)
|
||||
} else {
|
||||
utf8Lit := getUtf8Literal(ch)
|
||||
dsqWord.writeString(utf8Lit)
|
||||
}
|
||||
return
|
||||
}
|
||||
ec.CurWord.writeRune(ch)
|
||||
buf.WriteRune(ch)
|
||||
return
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ const (
|
||||
WordTypeLit = "lit" // (can-extend)
|
||||
WordTypeOp = "op" // single: & ; | ( ) < > \n multi(2): && || ;; << >> <& >& <> >| (( multi(3): <<- ('((' requires special processing)
|
||||
WordTypeKey = "key" // if then else elif fi do done case esac while until for in { } ! (( [[
|
||||
WordTypeGroup = "grp" // contains other words e.g. "hello"foo'bar'$x (has-subs)
|
||||
WordTypeGroup = "grp" // contains other words e.g. "hello"foo'bar'$x (has-subs) (can-extend)
|
||||
WordTypeSimpleVar = "svar" // simplevar $ (can-extend)
|
||||
|
||||
WordTypeDQ = "dq" // " (quote-context) (can-extend) (has-subs)
|
||||
@ -113,6 +113,20 @@ type wordMeta struct {
|
||||
QuoteContext bool
|
||||
}
|
||||
|
||||
func (m wordMeta) getSuffix() string {
|
||||
if m.SuffixLen == 0 {
|
||||
return ""
|
||||
}
|
||||
return string(m.EmptyWord[len(m.EmptyWord)-m.SuffixLen:])
|
||||
}
|
||||
|
||||
func (m wordMeta) getPrefix() string {
|
||||
if m.PrefixLen == 0 {
|
||||
return ""
|
||||
}
|
||||
return string(m.EmptyWord[:m.PrefixLen])
|
||||
}
|
||||
|
||||
func makeWordMeta(wtype string, emptyWord string, prefixLen int, suffixLen int, canExtend bool, quoteContext bool) {
|
||||
if len(emptyWord) != prefixLen+suffixLen {
|
||||
panic(fmt.Sprintf("invalid empty word %s %d %d", emptyWord, prefixLen, suffixLen))
|
||||
@ -140,14 +154,18 @@ func init() {
|
||||
makeWordMeta(WordTypeDB, "$[]", 2, 1, false, false)
|
||||
}
|
||||
|
||||
func MakeEmptyWord(wtype string, qc QuoteContext, offset int) *WordType {
|
||||
func MakeEmptyWord(wtype string, qc QuoteContext, offset int, complete bool) *WordType {
|
||||
meta := wordMetaMap[wtype]
|
||||
if meta.Type == "" {
|
||||
meta = wordMetaMap[WordTypeRaw]
|
||||
}
|
||||
rtn := &WordType{Type: meta.Type, QC: qc, Offset: offset, Complete: true}
|
||||
rtn := &WordType{Type: meta.Type, QC: qc, Offset: offset, Complete: complete}
|
||||
if len(meta.EmptyWord) > 0 {
|
||||
rtn.Raw = append([]rune(nil), meta.EmptyWord...)
|
||||
if complete {
|
||||
rtn.Raw = append([]rune(nil), meta.EmptyWord...)
|
||||
} else {
|
||||
rtn.Raw = append([]rune(nil), []rune(meta.getPrefix())...)
|
||||
}
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
|
@ -57,33 +57,46 @@ func lastWord(words []*WordType) *WordType {
|
||||
return words[len(words)-1]
|
||||
}
|
||||
|
||||
func testExtend(t *testing.T, startStr string, extendStr string, expectedStr string) {
|
||||
words := Tokenize(startStr)
|
||||
ec := makeExtendContext(nil, lastWord(words))
|
||||
for _, ch := range extendStr {
|
||||
ec.extend(ch)
|
||||
func testExtend(t *testing.T, startStr string, extendStr string, complete bool, expStr string) {
|
||||
startSP := utilfn.ParseToSP(startStr)
|
||||
words := Tokenize(startSP.Str)
|
||||
word := findCompletionWordAtPos(words, startSP.Pos, true)
|
||||
if word == nil {
|
||||
word = MakeEmptyWord(WordTypeLit, nil, startSP.Pos, true)
|
||||
}
|
||||
ec.ensureCurWord()
|
||||
output := wordsToStr(ec.Rtn)
|
||||
fmt.Printf("[%s] + [%s] => [%s]\n", startStr, extendStr, output)
|
||||
if output != expectedStr {
|
||||
t.Errorf("extension does not match: [%s] + [%s] => [%s] expected [%s]\n", startStr, extendStr, output, expectedStr)
|
||||
outSP := Extend(word, startSP.Pos-word.Offset, extendStr, complete)
|
||||
expSP := utilfn.ParseToSP(expStr)
|
||||
fmt.Printf("extend: [%s] + [%s] => [%s]\n", startStr, extendStr, outSP)
|
||||
if outSP != expSP {
|
||||
t.Errorf("extension does not match: [%s] + [%s] => [%s] expected [%s]\n", startStr, extendStr, outSP, expSP)
|
||||
}
|
||||
}
|
||||
|
||||
func Test2(t *testing.T) {
|
||||
testExtend(t, `'he'`, "llo", `'hello'`)
|
||||
testExtend(t, `'he'`, "'", `'he'\'''`)
|
||||
testExtend(t, `'he'`, "'\x01", `'he'\'$'\x01'''`)
|
||||
testExtend(t, `he`, "llo", `hello`)
|
||||
testExtend(t, `he`, "l*l'\x01\x07o", `hel\*l\'$'\x01'$'\a'o`)
|
||||
testExtend(t, `$x`, "fo|o", `$xfoo`)
|
||||
testExtend(t, `${x`, "fo|o", `${xfoo`)
|
||||
testExtend(t, `$'f`, "oo", `$'foo`)
|
||||
testExtend(t, `$'f`, "'\x01\x07o", `$'f\'\x01\ao`)
|
||||
testExtend(t, `"f"`, "oo", `"foo"`)
|
||||
testExtend(t, `"mi"`, "ke's \"hello\"", `"mike's \"hello\""`)
|
||||
testExtend(t, `"t"`, "t\x01\x07", `"tt"$'\x01'$'\a'""`)
|
||||
testExtend(t, `he[*]`, "llo", false, "hello[*]")
|
||||
testExtend(t, `he[*]`, "llo", true, "hello [*]")
|
||||
testExtend(t, `'mi[*]e`, "k", false, "'mik[*]e")
|
||||
testExtend(t, `'mi[*]e`, "k", true, "'mik[*]e")
|
||||
testExtend(t, `'mi[*]'`, "ke", true, "'mike' [*]")
|
||||
testExtend(t, `'mi'[*]`, "ke", true, "'mike' [*]")
|
||||
testExtend(t, `'mi[*]'`, "ke", false, "'mike[*]'")
|
||||
testExtend(t, `'mi'[*]`, "ke", false, "'mike[*]'")
|
||||
testExtend(t, `$f[*]`, "oo", false, "$foo[*]")
|
||||
testExtend(t, `${f}[*]`, "oo", false, "${foo[*]}")
|
||||
testExtend(t, `${f[*]}`, "oo", true, "${foo} [*]")
|
||||
|
||||
// testExtend(t, `'he'`, "llo", `'hello'`)
|
||||
// testExtend(t, `'he'`, "'", `'he'\'''`)
|
||||
// testExtend(t, `'he'`, "'\x01", `'he'\'$'\x01'''`)
|
||||
// testExtend(t, `he`, "llo", `hello`)
|
||||
// testExtend(t, `he`, "l*l'\x01\x07o", `hel\*l\'$'\x01'$'\a'o`)
|
||||
// testExtend(t, `$x`, "fo|o", `$xfoo`)
|
||||
// testExtend(t, `${x`, "fo|o", `${xfoo`)
|
||||
// testExtend(t, `$'f`, "oo", `$'foo`)
|
||||
// testExtend(t, `$'f`, "'\x01\x07o", `$'f\'\x01\ao`)
|
||||
// testExtend(t, `"f"`, "oo", `"foo"`)
|
||||
// testExtend(t, `"mi"`, "ke's \"hello\"", `"mike's \"hello\""`)
|
||||
// testExtend(t, `"t"`, "t\x01\x07", `"tt"$'\x01'$'\a'""`)
|
||||
}
|
||||
|
||||
func testParseCommands(t *testing.T, str string) {
|
||||
@ -151,6 +164,8 @@ func TestCompPos(t *testing.T) {
|
||||
testCompPos(t, "ls ${abc:$(ls -l [*])}", CompTypeVar, false, 0, true) // we don't sub-parse inside of ${} (so this returns "var" right now)
|
||||
testCompPos(t, `ls abc"$(ls $"echo $(ls ./[*]x) foo)" `, CompTypeArg, true, 1, true)
|
||||
testCompPos(t, `ls "abc$d[*]"`, CompTypeVar, false, 0, true)
|
||||
testCompPos(t, `ls "abc$d$'a[*]`, CompTypeArg, true, 1, true)
|
||||
testCompPos(t, `ls $[*]'foo`, CompTypeInvalid, false, 0, false)
|
||||
}
|
||||
|
||||
func testExpand(t *testing.T, str string, pos int, expStr string, expInfo *ExpandInfo) {
|
||||
|
Loading…
Reference in New Issue
Block a user