mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-02-22 02:41:23 +01:00
checkpoint, testing new compgen. fixed superoffset bug
This commit is contained in:
parent
bb3e12fee7
commit
bd3595c954
@ -1172,7 +1172,7 @@ func CompGenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto
|
||||
pos = len(cmdLine)
|
||||
}
|
||||
showComps := resolveBool(pk.Kwargs["compshow"], false)
|
||||
cmdSP := comp.StrWithPos{Str: cmdLine, Pos: pos}
|
||||
cmdSP := utilfn.StrWithPos{Str: cmdLine, Pos: pos}
|
||||
compCtx := comp.CompContext{}
|
||||
if ids.Remote != nil {
|
||||
rptr := ids.Remote.RemotePtr
|
||||
@ -1185,39 +1185,19 @@ func CompGenCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (ssto
|
||||
return nil, err
|
||||
}
|
||||
if crtn == nil {
|
||||
return nil, fmt.Errorf("no return value from DoCompGen")
|
||||
return nil, nil
|
||||
}
|
||||
if showComps || newSP == nil {
|
||||
if showComps {
|
||||
compStrs := crtn.GetCompDisplayStrs()
|
||||
return makeInfoFromComps(crtn.CompType, compStrs, crtn.HasMore), nil
|
||||
}
|
||||
if newSP == nil || cmdSP == *newSP {
|
||||
return nil, nil
|
||||
}
|
||||
update := sstore.ModelUpdate{
|
||||
CmdLine: &sstore.CmdLineType{CmdLine: newSP.Str, CursorPos: newSP.Pos},
|
||||
}
|
||||
return update, nil
|
||||
|
||||
// prefix := cmdLine[:pos]
|
||||
// parts := strings.Split(prefix, " ")
|
||||
// compType := "file"
|
||||
// if len(parts) > 0 && len(parts) < 2 && strings.HasPrefix(parts[0], "/") {
|
||||
// compType = "metacommand"
|
||||
// } else if len(parts) == 2 && (parts[0] == "cd" || parts[0] == "/cd") {
|
||||
// compType = "directory"
|
||||
// } else if len(parts) <= 1 {
|
||||
// compType = "command"
|
||||
// }
|
||||
// lastPart := ""
|
||||
// if len(parts) > 0 {
|
||||
// lastPart = parts[len(parts)-1]
|
||||
// }
|
||||
// comps, hasMore, err := doCompGen(ctx, pk, lastPart, compType, showComps)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if showComps {
|
||||
// return makeInfoFromComps(compType, comps, hasMore), nil
|
||||
// }
|
||||
// return makeInsertUpdateFromComps(int64(pos), lastPart, comps, hasMore), nil
|
||||
}
|
||||
|
||||
func CommentCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/scripthaus-dev/mshell/pkg/shexec"
|
||||
"github.com/scripthaus-dev/mshell/pkg/simpleexpand"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scpacket"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
@ -124,9 +125,9 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
||||
strReader := strings.NewReader(bracketStr)
|
||||
parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
|
||||
var wordErr error
|
||||
var ectx shexec.SimpleExpandContext // do not set HomeDir (we don't expand ~ in bracket args)
|
||||
var ectx simpleexpand.SimpleExpandContext // do not set HomeDir (we don't expand ~ in bracket args)
|
||||
err := parser.Words(strReader, func(w *syntax.Word) bool {
|
||||
litStr := shexec.SimpleExpandWord(ectx, w, bracketStr)
|
||||
litStr, _ := simpleexpand.SimpleExpandWord(ectx, w, bracketStr)
|
||||
eqIdx := strings.Index(litStr, "=")
|
||||
var varName, varVal string
|
||||
if eqIdx == -1 {
|
||||
@ -285,8 +286,8 @@ func parseAliasStmt(stmt *syntax.Stmt, sourceStr string) (string, string, error)
|
||||
return "", "", fmt.Errorf("invalid alias cmd word (not 'alias')")
|
||||
}
|
||||
secondWord := callExpr.Args[1]
|
||||
var ectx shexec.SimpleExpandContext // no homedir, do not want ~ expansion
|
||||
val := shexec.SimpleExpandWord(ectx, secondWord, sourceStr)
|
||||
var ectx simpleexpand.SimpleExpandContext // no homedir, do not want ~ expansion
|
||||
val, _ := simpleexpand.SimpleExpandWord(ectx, secondWord, sourceStr)
|
||||
eqIdx := strings.Index(val, "=")
|
||||
if eqIdx == -1 {
|
||||
return "", "", fmt.Errorf("no '=' in alias definition")
|
||||
|
273
pkg/comp/comp.go
273
pkg/comp/comp.go
@ -9,9 +9,11 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"github.com/scripthaus-dev/mshell/pkg/shexec"
|
||||
"github.com/scripthaus-dev/mshell/pkg/simpleexpand"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/shparse"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
@ -48,11 +50,6 @@ type CompContext struct {
|
||||
ForDisplay bool
|
||||
}
|
||||
|
||||
type StrWithPos struct {
|
||||
Str string
|
||||
Pos int
|
||||
}
|
||||
|
||||
type ParsedWord struct {
|
||||
Offset int
|
||||
Word *syntax.Word
|
||||
@ -81,6 +78,28 @@ type CompReturn struct {
|
||||
HasMore bool
|
||||
}
|
||||
|
||||
var noEscChars []bool
|
||||
var specialEsc []string
|
||||
|
||||
func init() {
|
||||
noEscChars = make([]bool, 256)
|
||||
for ch := 0; ch < 256; ch++ {
|
||||
if (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
|
||||
ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '=' {
|
||||
noEscChars[byte(ch)] = true
|
||||
}
|
||||
}
|
||||
specialEsc = make([]string, 256)
|
||||
specialEsc[0x7] = "\\a"
|
||||
specialEsc[0x8] = "\\b"
|
||||
specialEsc[0x9] = "\\t"
|
||||
specialEsc[0xa] = "\\n"
|
||||
specialEsc[0xb] = "\\v"
|
||||
specialEsc[0xc] = "\\f"
|
||||
specialEsc[0xd] = "\\r"
|
||||
specialEsc[0x1b] = "\\E"
|
||||
}
|
||||
|
||||
func compQuoteDQString(s string, close bool) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('"')
|
||||
@ -98,15 +117,108 @@ func compQuoteDQString(s string, close bool) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func hasGlob(s string) bool {
|
||||
var lastExtGlob bool
|
||||
for _, ch := range s {
|
||||
if ch == '*' || ch == '?' || ch == '[' || ch == '{' {
|
||||
return true
|
||||
}
|
||||
if ch == '+' || ch == '@' || ch == '!' {
|
||||
lastExtGlob = true
|
||||
continue
|
||||
}
|
||||
if lastExtGlob && ch == '(' {
|
||||
return true
|
||||
}
|
||||
lastExtGlob = false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func writeUtf8Literal(buf *bytes.Buffer, ch rune) {
|
||||
var runeArr [utf8.UTFMax]byte
|
||||
buf.WriteString("$'")
|
||||
barr := runeArr[:]
|
||||
byteLen := utf8.EncodeRune(barr, ch)
|
||||
for i := 0; i < byteLen; i++ {
|
||||
buf.WriteString("\\x")
|
||||
buf.WriteByte(utilfn.HexDigits[barr[i]/16])
|
||||
buf.WriteByte(utilfn.HexDigits[barr[i]%16])
|
||||
}
|
||||
buf.WriteByte('\'')
|
||||
}
|
||||
|
||||
func compQuoteLiteralString(s string) string {
|
||||
var buf bytes.Buffer
|
||||
for idx, ch := range s {
|
||||
if ch == 0 {
|
||||
break
|
||||
}
|
||||
if idx == 0 && ch == '~' {
|
||||
buf.WriteRune(ch)
|
||||
continue
|
||||
}
|
||||
if ch > unicode.MaxASCII {
|
||||
writeUtf8Literal(&buf, ch)
|
||||
continue
|
||||
}
|
||||
var bch = byte(ch)
|
||||
if noEscChars[bch] {
|
||||
buf.WriteRune(ch)
|
||||
continue
|
||||
}
|
||||
if specialEsc[bch] != "" {
|
||||
buf.WriteString(specialEsc[bch])
|
||||
continue
|
||||
}
|
||||
if !unicode.IsPrint(ch) {
|
||||
writeUtf8Literal(&buf, ch)
|
||||
continue
|
||||
}
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(bch)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func compQuoteSQString(s string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, ch := range s {
|
||||
if ch == 0 {
|
||||
break
|
||||
}
|
||||
if ch == '\'' {
|
||||
buf.WriteString("'\\''")
|
||||
continue
|
||||
}
|
||||
var bch byte
|
||||
if ch <= unicode.MaxASCII {
|
||||
bch = byte(ch)
|
||||
}
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) {
|
||||
buf.WriteByte('\'')
|
||||
if bch != 0 && specialEsc[bch] != "" {
|
||||
buf.WriteString(specialEsc[bch])
|
||||
} else {
|
||||
writeUtf8Literal(&buf, ch)
|
||||
}
|
||||
buf.WriteByte('\'')
|
||||
continue
|
||||
}
|
||||
buf.WriteByte(bch)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func compQuoteString(s string, quoteType string, close bool) string {
|
||||
if quoteType != QuoteTypeANSI {
|
||||
if quoteType != QuoteTypeANSI && quoteType != QuoteTypeLiteral {
|
||||
for _, ch := range s {
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) || ch == '!' {
|
||||
quoteType = QuoteTypeANSI
|
||||
break
|
||||
}
|
||||
if ch == '\'' {
|
||||
if quoteType == QuoteTypeSQ || quoteType == QuoteTypeLiteral {
|
||||
if quoteType == QuoteTypeSQ {
|
||||
quoteType = QuoteTypeANSI
|
||||
break
|
||||
}
|
||||
@ -122,11 +234,7 @@ func compQuoteString(s string, quoteType string, close bool) string {
|
||||
return rtn
|
||||
}
|
||||
if quoteType == QuoteTypeLiteral {
|
||||
rtn := utilfn.ShellQuote(s, false, MaxCompQuoteLen)
|
||||
if len(rtn) > 0 && rtn[0] == '\'' && !close {
|
||||
rtn = rtn[0 : len(rtn)-1]
|
||||
}
|
||||
return rtn
|
||||
return compQuoteLiteralString(s)
|
||||
}
|
||||
if quoteType == QuoteTypeSQ {
|
||||
rtn := utilfn.ShellQuote(s, false, MaxCompQuoteLen)
|
||||
@ -149,12 +257,12 @@ func (p *CompPoint) wordAsStr(w ParsedWord) string {
|
||||
return w.PartialWord
|
||||
}
|
||||
|
||||
func (p *CompPoint) simpleExpandWord(w ParsedWord) string {
|
||||
ectx := shexec.SimpleExpandContext{}
|
||||
func (p *CompPoint) simpleExpandWord(w ParsedWord) (string, simpleexpand.SimpleExpandInfo) {
|
||||
ectx := simpleexpand.SimpleExpandContext{}
|
||||
if w.Word != nil {
|
||||
return shexec.SimpleExpandWord(ectx, w.Word, p.StmtStr)
|
||||
return simpleexpand.SimpleExpandWord(ectx, w.Word, p.StmtStr)
|
||||
}
|
||||
return shexec.SimpleExpandPartialWord(ectx, w.PartialWord, false)
|
||||
return simpleexpand.SimpleExpandPartialWord(ectx, w.PartialWord, false)
|
||||
}
|
||||
|
||||
func getQuoteTypePref(str string) string {
|
||||
@ -170,9 +278,9 @@ func getQuoteTypePref(str string) string {
|
||||
return QuoteTypeLiteral
|
||||
}
|
||||
|
||||
func (p *CompPoint) getCompPrefix() string {
|
||||
func (p *CompPoint) getCompPrefix() (string, simpleexpand.SimpleExpandInfo) {
|
||||
if p.CompWordPos == 0 {
|
||||
return ""
|
||||
return "", simpleexpand.SimpleExpandInfo{}
|
||||
}
|
||||
pword := p.Words[p.CompWord]
|
||||
wordStr := p.wordAsStr(pword)
|
||||
@ -184,10 +292,10 @@ func (p *CompPoint) getCompPrefix() string {
|
||||
// and a partial on just the current part. this is an uncommon case though
|
||||
// and has very little upside (even bash does not expand multipart words correctly)
|
||||
partialWordStr := wordStr[:p.CompWordPos]
|
||||
return shexec.SimpleExpandPartialWord(shexec.SimpleExpandContext{}, partialWordStr, false)
|
||||
return simpleexpand.SimpleExpandPartialWord(simpleexpand.SimpleExpandContext{}, partialWordStr, false)
|
||||
}
|
||||
|
||||
func (p *CompPoint) extendWord(newWord string, newWordComplete bool) StrWithPos {
|
||||
func (p *CompPoint) extendWord(newWord string, newWordComplete bool) utilfn.StrWithPos {
|
||||
pword := p.Words[p.CompWord]
|
||||
wordStr := p.wordAsStr(pword)
|
||||
quotePref := getQuoteTypePref(wordStr)
|
||||
@ -198,18 +306,31 @@ func (p *CompPoint) extendWord(newWord string, newWordComplete bool) StrWithPos
|
||||
newQuotedStr = newQuotedStr + " "
|
||||
}
|
||||
newPos := len(newQuotedStr)
|
||||
return StrWithPos{Str: newQuotedStr + wordSuffix, Pos: newPos}
|
||||
return utilfn.StrWithPos{Str: newQuotedStr + wordSuffix, Pos: newPos}
|
||||
}
|
||||
|
||||
func (p *CompPoint) FullyExtend(crtn *CompReturn) StrWithPos {
|
||||
// returns (extension, complete)
|
||||
func computeCompExtension(compPrefix string, crtn *CompReturn) (string, bool) {
|
||||
if crtn == nil || crtn.HasMore {
|
||||
return StrWithPos{Str: p.getOrigStr(), Pos: p.getOrigPos()}
|
||||
return "", false
|
||||
}
|
||||
compStrs := crtn.GetCompStrs()
|
||||
compPrefix := p.getCompPrefix()
|
||||
lcp := utilfn.LongestPrefix(compPrefix, compStrs)
|
||||
if lcp == compPrefix || len(lcp) < len(compPrefix) || !strings.HasPrefix(lcp, compPrefix) {
|
||||
return StrWithPos{Str: p.getOrigStr(), Pos: p.getOrigPos()}
|
||||
return "", false
|
||||
}
|
||||
return lcp[len(compPrefix):], utilfn.ContainsStr(compStrs, lcp)
|
||||
}
|
||||
|
||||
func (p *CompPoint) FullyExtend(crtn *CompReturn) utilfn.StrWithPos {
|
||||
if crtn == nil || crtn.HasMore {
|
||||
return utilfn.StrWithPos{Str: p.getOrigStr(), Pos: p.getOrigPos()}
|
||||
}
|
||||
compStrs := crtn.GetCompStrs()
|
||||
compPrefix, _ := p.getCompPrefix()
|
||||
lcp := utilfn.LongestPrefix(compPrefix, compStrs)
|
||||
if lcp == compPrefix || len(lcp) < len(compPrefix) || !strings.HasPrefix(lcp, compPrefix) {
|
||||
return utilfn.StrWithPos{Str: p.getOrigStr(), Pos: p.getOrigPos()}
|
||||
}
|
||||
newStr := p.extendWord(lcp, utilfn.ContainsStr(compStrs, lcp))
|
||||
var buf bytes.Buffer
|
||||
@ -226,7 +347,7 @@ func (p *CompPoint) FullyExtend(crtn *CompReturn) StrWithPos {
|
||||
buf.WriteString(p.Suffix)
|
||||
compWord := p.Words[p.CompWord]
|
||||
newPos := len(p.Prefix) + compWord.Offset + len(compWord.Prefix) + newStr.Pos
|
||||
return StrWithPos{Str: buf.String(), Pos: newPos}
|
||||
return utilfn.StrWithPos{Str: buf.String(), Pos: newPos}
|
||||
}
|
||||
|
||||
func (p *CompPoint) dump() {
|
||||
@ -240,7 +361,7 @@ func (p *CompPoint) dump() {
|
||||
fmt.Printf("{%s}", w.Prefix)
|
||||
}
|
||||
if idx == p.CompWord {
|
||||
fmt.Printf("%s\n", strWithCursor(p.wordAsStr(w), p.CompWordPos))
|
||||
fmt.Printf("%s\n", utilfn.StrWithPos{Str: p.wordAsStr(w), Pos: p.CompWordPos})
|
||||
} else {
|
||||
fmt.Printf("%s\n", p.wordAsStr(w))
|
||||
}
|
||||
@ -253,24 +374,6 @@ func (p *CompPoint) dump() {
|
||||
|
||||
var SimpleCompGenFns map[string]SimpleCompGenFnType
|
||||
|
||||
func (sp StrWithPos) String() string {
|
||||
return strWithCursor(sp.Str, sp.Pos)
|
||||
}
|
||||
|
||||
func strWithCursor(str string, pos int) string {
|
||||
if pos < 0 {
|
||||
return "[*]_" + str
|
||||
}
|
||||
if pos >= len(str) {
|
||||
if pos > len(str) {
|
||||
return str + "_[*]"
|
||||
}
|
||||
return str + "[*]"
|
||||
} else {
|
||||
return str[:pos] + "[*]" + str[pos:]
|
||||
}
|
||||
}
|
||||
|
||||
func isWhitespace(str string) bool {
|
||||
return strings.TrimSpace(str) == ""
|
||||
}
|
||||
@ -284,7 +387,7 @@ func splitInitialWhitespace(str string) (string, string) {
|
||||
return str, ""
|
||||
}
|
||||
|
||||
func ParseCompPoint(cmdStr StrWithPos) *CompPoint {
|
||||
func ParseCompPoint(cmdStr utilfn.StrWithPos) *CompPoint {
|
||||
fullCmdStr := cmdStr.Str
|
||||
pos := cmdStr.Pos
|
||||
// fmt.Printf("---\n")
|
||||
@ -388,14 +491,86 @@ func splitCompWord(p *CompPoint) {
|
||||
p.Words = newWords
|
||||
}
|
||||
|
||||
func DoCompGen(ctx context.Context, sp StrWithPos, compCtx CompContext) (*CompReturn, *StrWithPos, error) {
|
||||
func getCompType(compPos shparse.CompletionPos) string {
|
||||
switch compPos.CompType {
|
||||
case shparse.CompTypeCommandMeta:
|
||||
return CGTypeCommandMeta
|
||||
|
||||
case shparse.CompTypeCommand:
|
||||
return CGTypeCommand
|
||||
|
||||
case shparse.CompTypeVar:
|
||||
return CGTypeVariable
|
||||
|
||||
case shparse.CompTypeArg, shparse.CompTypeBasic, shparse.CompTypeAssignment:
|
||||
return CGTypeFile
|
||||
|
||||
default:
|
||||
return CGTypeFile
|
||||
}
|
||||
}
|
||||
|
||||
func fixupVarPrefix(varPrefix string) string {
|
||||
if strings.HasPrefix(varPrefix, "${") {
|
||||
varPrefix = varPrefix[2:]
|
||||
if strings.HasSuffix(varPrefix, "}") {
|
||||
varPrefix = varPrefix[:len(varPrefix)-1]
|
||||
}
|
||||
} else if strings.HasPrefix(varPrefix, "$") {
|
||||
varPrefix = varPrefix[1:]
|
||||
}
|
||||
return varPrefix
|
||||
}
|
||||
|
||||
func DoCompGen(ctx context.Context, cmdStr utilfn.StrWithPos, compCtx CompContext) (*CompReturn, *utilfn.StrWithPos, error) {
|
||||
words := shparse.Tokenize(cmdStr.Str)
|
||||
cmds := shparse.ParseCommands(words)
|
||||
compPos := shparse.FindCompletionPos(cmds, cmdStr.Pos, 0)
|
||||
fmt.Printf("comppos: %v\n", compPos)
|
||||
if compPos.CompType == shparse.CompTypeInvalid {
|
||||
return nil, nil, nil
|
||||
}
|
||||
var compPrefix string
|
||||
if compPos.CompWord != nil {
|
||||
var info shparse.ExpandInfo
|
||||
compPrefix, info = shparse.SimpleExpandPrefix(shparse.ExpandContext{}, compPos.CompWord, compPos.CompWordOffset)
|
||||
if info.HasGlob || info.HasExtGlob || info.HasHistory || info.HasSpecial {
|
||||
return nil, nil, nil
|
||||
}
|
||||
if compPos.CompType != shparse.CompTypeVar && info.HasVar {
|
||||
return nil, nil, nil
|
||||
}
|
||||
if compPos.CompType == shparse.CompTypeVar {
|
||||
compPrefix = fixupVarPrefix(compPrefix)
|
||||
}
|
||||
}
|
||||
scType := getCompType(compPos)
|
||||
crtn, err := DoSimpleComp(ctx, scType, compPrefix, compCtx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if compCtx.ForDisplay {
|
||||
return crtn, nil, nil
|
||||
}
|
||||
extensionStr, extensionComplete := computeCompExtension(compPrefix, crtn)
|
||||
if extensionStr == "" {
|
||||
return crtn, nil, nil
|
||||
}
|
||||
rtnSP := compPos.Extend(cmdStr, extensionStr, extensionComplete)
|
||||
return crtn, &rtnSP, nil
|
||||
}
|
||||
|
||||
func DoCompGenOld(ctx context.Context, sp utilfn.StrWithPos, compCtx CompContext) (*CompReturn, *utilfn.StrWithPos, error) {
|
||||
compPoint := ParseCompPoint(sp)
|
||||
compType := CGTypeFile
|
||||
if compPoint.CompWord == 0 {
|
||||
compType = CGTypeCommandMeta
|
||||
}
|
||||
// TODO lookup special types
|
||||
compPrefix := compPoint.getCompPrefix()
|
||||
compPrefix, info := compPoint.getCompPrefix()
|
||||
if info.HasVar || info.HasGlob || info.HasExtGlob || info.HasHistory || info.HasSpecial {
|
||||
return nil, nil, nil
|
||||
}
|
||||
crtn, err := DoSimpleComp(ctx, compType, compPrefix, compCtx, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -1,5 +1,12 @@
|
||||
package shparse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
)
|
||||
|
||||
const (
|
||||
CompTypeCommandMeta = "command-meta"
|
||||
CompTypeCommand = "command"
|
||||
@ -156,38 +163,38 @@ func findCompletionWordAtPos(words []*WordType, pos int, allowEndMatch bool) *Wo
|
||||
|
||||
// recursively descend down the word, parse commands and find a sub completion point if any.
|
||||
// return nil if there is no sub completion point in this word
|
||||
func findCompletionPosInWord(word *WordType, pos int, superOffset int) *CompletionPos {
|
||||
func findCompletionPosInWord(word *WordType, posInWord int, superOffset int) *CompletionPos {
|
||||
rawPos := word.Offset + posInWord
|
||||
if word.Type == WordTypeGroup || word.Type == WordTypeDQ || word.Type == WordTypeDDQ {
|
||||
// need to descend further
|
||||
if pos <= word.contentStartPos() {
|
||||
if posInWord <= word.contentStartPos() {
|
||||
return nil
|
||||
}
|
||||
if pos > word.contentEndPos() {
|
||||
if posInWord > word.contentEndPos() {
|
||||
return nil
|
||||
}
|
||||
subWord := findCompletionWordAtPos(word.Subs, pos-word.contentStartPos(), false)
|
||||
subWord := findCompletionWordAtPos(word.Subs, posInWord-word.contentStartPos(), false)
|
||||
if subWord == nil {
|
||||
return nil
|
||||
}
|
||||
fullOffset := subWord.Offset + word.contentStartPos()
|
||||
return findCompletionPosInWord(subWord, pos-fullOffset, superOffset+fullOffset)
|
||||
return findCompletionPosInWord(subWord, posInWord-(subWord.Offset+word.contentStartPos()), superOffset+(word.Offset+word.contentStartPos()))
|
||||
}
|
||||
if word.Type == WordTypeDP || word.Type == WordTypeBQ {
|
||||
if pos < word.contentStartPos() {
|
||||
if posInWord < word.contentStartPos() {
|
||||
return nil
|
||||
}
|
||||
if pos > word.contentEndPos() {
|
||||
if posInWord > word.contentEndPos() {
|
||||
return nil
|
||||
}
|
||||
subCmds := ParseCommands(word.Subs)
|
||||
newPos := FindCompletionPos(subCmds, pos-word.contentStartPos(), superOffset+word.contentStartPos())
|
||||
newPos := FindCompletionPos(subCmds, posInWord-word.contentStartPos(), superOffset+(word.Offset+word.contentStartPos()))
|
||||
return &newPos
|
||||
}
|
||||
if word.Type == WordTypeSimpleVar || word.Type == WordTypeVarBrace {
|
||||
// special "var" completion
|
||||
rtn := &CompletionPos{RawPos: pos, SuperOffset: superOffset}
|
||||
rtn := &CompletionPos{RawPos: rawPos, SuperOffset: superOffset}
|
||||
rtn.CompType = CompTypeVar
|
||||
rtn.CompWordOffset = pos
|
||||
rtn.CompWordOffset = posInWord
|
||||
rtn.CompWord = word
|
||||
return rtn
|
||||
}
|
||||
@ -250,10 +257,27 @@ func FindCompletionPos(cmds []*CmdType, pos int, superOffset int) CompletionPos
|
||||
if cpos.CompWord == nil {
|
||||
return cpos
|
||||
}
|
||||
subPos := findCompletionPosInWord(cpos.CompWord, cpos.CompWordOffset, superOffset+cpos.CompWord.Offset)
|
||||
subPos := findCompletionPosInWord(cpos.CompWord, cpos.CompWordOffset, superOffset)
|
||||
if subPos == nil {
|
||||
return cpos
|
||||
} else {
|
||||
return *subPos
|
||||
}
|
||||
}
|
||||
|
||||
func (cpos CompletionPos) Extend(origStr utilfn.StrWithPos, extensionStr string, extensionComplete bool) utilfn.StrWithPos {
|
||||
compWord := cpos.CompWord
|
||||
if compWord == nil {
|
||||
compWord = MakeEmptyWord(WordTypeLit, nil, cpos.RawPos, true)
|
||||
}
|
||||
realOffset := compWord.Offset + cpos.SuperOffset
|
||||
fmt.Printf("cpos-extend: %d[%s] ext[%s] cword[%v] off:%d super:%d real:%d\n", len([]rune(origStr.Str)), origStr, extensionStr, compWord, compWord.Offset, cpos.SuperOffset, realOffset)
|
||||
if strings.HasSuffix(extensionStr, "/") {
|
||||
extensionComplete = false
|
||||
}
|
||||
rtnSP := Extend(compWord, cpos.CompWordOffset, extensionStr, extensionComplete)
|
||||
origRunes := []rune(origStr.Str)
|
||||
rtnSP = rtnSP.Prepend(string(origRunes[0:realOffset]))
|
||||
rtnSP = rtnSP.Append(string(origRunes[realOffset+len(compWord.Raw):]))
|
||||
return rtnSP
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ func TestCmd(t *testing.T) {
|
||||
testParseCommands(t, `x="foo $y" z=10 ls`)
|
||||
}
|
||||
|
||||
func testCompPos(t *testing.T, cmdStr string, compType string, hasCommand bool, cmdWordPos int, hasWord bool) {
|
||||
func testCompPos(t *testing.T, cmdStr string, compType string, hasCommand bool, cmdWordPos int, hasWord bool, superOffset int) {
|
||||
cmdSP := utilfn.ParseToSP(cmdStr)
|
||||
words := Tokenize(cmdSP.Str)
|
||||
cmds := ParseCommands(words)
|
||||
@ -161,24 +161,28 @@ func testCompPos(t *testing.T, cmdStr string, compType string, hasCommand bool,
|
||||
if cpos.CmdWordPos != cmdWordPos {
|
||||
t.Errorf("testCompPos %q => bad cmd-word-pos got:%d exp:%d", cmdStr, cpos.CmdWordPos, cmdWordPos)
|
||||
}
|
||||
if cpos.SuperOffset != superOffset {
|
||||
t.Errorf("testCompPos %q => bad super-offset got:%d exp:%d", cmdStr, cpos.SuperOffset, superOffset)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompPos(t *testing.T) {
|
||||
testCompPos(t, "ls [*]foo", CompTypeArg, true, 1, false)
|
||||
testCompPos(t, "ls foo [*];", CompTypeArg, true, 2, false)
|
||||
testCompPos(t, "ls foo ;[*]", CompTypeCommand, false, 0, false)
|
||||
testCompPos(t, "ls foo >[*]> ./bar", CompTypeInvalid, true, 2, true)
|
||||
testCompPos(t, "l[*]s", CompTypeCommand, true, 0, true)
|
||||
testCompPos(t, "ls[*]", CompTypeCommand, true, 0, true)
|
||||
testCompPos(t, "x=10 { (ls ./f[*] more); ls }", CompTypeArg, true, 1, true)
|
||||
testCompPos(t, "for x in 1[*] 2 3; do ", CompTypeBasic, false, 0, true)
|
||||
testCompPos(t, "for[*] x in 1 2 3;", CompTypeInvalid, false, 0, true)
|
||||
testCompPos(t, "ls \"abc $(ls -l t[*])\" && foo", CompTypeArg, true, 2, true)
|
||||
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)
|
||||
testCompPos(t, "ls [*]foo", CompTypeArg, true, 1, false, 0)
|
||||
testCompPos(t, "ls foo [*];", CompTypeArg, true, 2, false, 0)
|
||||
testCompPos(t, "ls foo ;[*]", CompTypeCommand, false, 0, false, 0)
|
||||
testCompPos(t, "ls foo >[*]> ./bar", CompTypeInvalid, true, 2, true, 0)
|
||||
testCompPos(t, "l[*]s", CompTypeCommand, true, 0, true, 0)
|
||||
testCompPos(t, "ls[*]", CompTypeCommand, true, 0, true, 0)
|
||||
testCompPos(t, "x=10 { (ls ./f[*] more); ls }", CompTypeArg, true, 1, true, 0)
|
||||
testCompPos(t, "for x in 1[*] 2 3; do ", CompTypeBasic, false, 0, true, 0)
|
||||
testCompPos(t, "for[*] x in 1 2 3;", CompTypeInvalid, false, 0, true, 0)
|
||||
testCompPos(t, `ls "abc $(ls -l t[*])" && foo`, CompTypeArg, true, 2, true, 10)
|
||||
testCompPos(t, "ls ${abc:$(ls -l [*])}", CompTypeVar, false, 0, true, 0) // 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, 21)
|
||||
testCompPos(t, `ls "abc$d[*]"`, CompTypeVar, false, 0, true, 4)
|
||||
testCompPos(t, `ls "abc$d$'a[*]`, CompTypeArg, true, 1, true, 0)
|
||||
testCompPos(t, `ls $[*]'foo`, CompTypeArg, true, 1, true, 0)
|
||||
testCompPos(t, `echo $TE[*]`, CompTypeVar, false, 0, true, 0)
|
||||
}
|
||||
|
||||
func testExpand(t *testing.T, str string, pos int, expStr string, expInfo *ExpandInfo) {
|
||||
|
Loading…
Reference in New Issue
Block a user