checkpoint on completion

This commit is contained in:
sawka 2022-11-09 20:38:28 -08:00
parent d225c988ed
commit 848f7164a3
3 changed files with 118 additions and 24 deletions

View File

@ -149,12 +149,9 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error {
strReader := strings.NewReader(bracketStr) strReader := strings.NewReader(bracketStr)
parser := syntax.NewParser(syntax.Variant(syntax.LangBash)) parser := syntax.NewParser(syntax.Variant(syntax.LangBash))
var wordErr error var wordErr error
var ectx shexec.SimpleExpandContext // do not set HomeDir (we don't expand ~ in bracket args)
err := parser.Words(strReader, func(w *syntax.Word) bool { err := parser.Words(strReader, func(w *syntax.Word) bool {
litStr, err := shexec.QuotedLitToStr(w) litStr := shexec.SimpleExpandWord(ectx, w, bracketStr)
if err != nil {
wordErr = fmt.Errorf("invalid expr in bracket args: %v", err)
return false
}
eqIdx := strings.Index(litStr, "=") eqIdx := strings.Index(litStr, "=")
var varName, varVal string var varName, varVal string
if eqIdx == -1 { if eqIdx == -1 {
@ -299,7 +296,7 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
return rtnPk, nil return rtnPk, nil
} }
func parseAliasStmt(stmt *syntax.Stmt) (string, string, error) { func parseAliasStmt(stmt *syntax.Stmt, sourceStr string) (string, string, error) {
cmd := stmt.Cmd cmd := stmt.Cmd
callExpr, ok := cmd.(*syntax.CallExpr) callExpr, ok := cmd.(*syntax.CallExpr)
if !ok { if !ok {
@ -313,10 +310,8 @@ func parseAliasStmt(stmt *syntax.Stmt) (string, string, error) {
return "", "", fmt.Errorf("invalid alias cmd word (not 'alias')") return "", "", fmt.Errorf("invalid alias cmd word (not 'alias')")
} }
secondWord := callExpr.Args[1] secondWord := callExpr.Args[1]
val, err := shexec.QuotedLitToStr(secondWord) var ectx shexec.SimpleExpandContext // no homedir, do not want ~ expansion
if err != nil { val := shexec.SimpleExpandWord(ectx, secondWord, sourceStr)
return "", "", err
}
eqIdx := strings.Index(val, "=") eqIdx := strings.Index(val, "=")
if eqIdx == -1 { if eqIdx == -1 {
return "", "", fmt.Errorf("no '=' in alias definition") return "", "", fmt.Errorf("no '=' in alias definition")
@ -333,7 +328,7 @@ func ParseAliases(aliases string) (map[string]string, error) {
} }
rtn := make(map[string]string) rtn := make(map[string]string)
for _, stmt := range file.Stmts { for _, stmt := range file.Stmts {
aliasName, aliasVal, err := parseAliasStmt(stmt) aliasName, aliasVal, err := parseAliasStmt(stmt, aliases)
if err != nil { if err != nil {
// fmt.Printf("stmt-err: %v\n", err) // fmt.Printf("stmt-err: %v\n", err)
continue continue

View File

@ -8,6 +8,7 @@ import (
"unicode" "unicode"
"github.com/scripthaus-dev/mshell/pkg/packet" "github.com/scripthaus-dev/mshell/pkg/packet"
"github.com/scripthaus-dev/mshell/pkg/shexec"
"github.com/scripthaus-dev/sh2-server/pkg/sstore" "github.com/scripthaus-dev/sh2-server/pkg/sstore"
"mvdan.cc/sh/v3/syntax" "mvdan.cc/sh/v3/syntax"
) )
@ -23,7 +24,13 @@ const (
SimpleCompGenTypeVariable = "variable" SimpleCompGenTypeVariable = "variable"
) )
type SimpleCompGenFnType = func(ctx context.Context, point SimpleCompPoint, rptr sstore.RemotePtrType, state *packet.ShellState, args []interface{}) type CompContext struct {
RemotePtr sstore.RemotePtrType
State *packet.ShellState
ForDisplay bool
}
type SimpleCompGenFnType = func(ctx context.Context, point SimpleCompPoint, compCtx CompContext, args []interface{}) (*CompReturn, error)
type SimpleCompPoint struct { type SimpleCompPoint struct {
Word string Word string
@ -31,12 +38,14 @@ type SimpleCompPoint struct {
} }
type ParsedWord struct { type ParsedWord struct {
Offset int Offset int
Word string Word *syntax.Word
Prefix string PartialWord string
Prefix string
} }
type CompPoint struct { type CompPoint struct {
StmtStr string
Words []ParsedWord Words []ParsedWord
CompWord int CompWord int
CompWordPos int CompWordPos int
@ -44,6 +53,38 @@ type CompPoint struct {
Suffix string Suffix string
} }
func (p *CompPoint) wordAsStr(w ParsedWord) string {
if w.Word != nil {
return p.StmtStr[w.Word.Pos().Offset():w.Word.End().Offset()]
}
return w.PartialWord
}
func (p *CompPoint) simpleExpandWord(w ParsedWord) string {
ectx := shexec.SimpleExpandContext{}
if w.Word != nil {
return SimpleExpandWord(ectx, w.Word, p.StmtStr)
}
return SimpleExpandPartialWord(ectx, p.PartialWord, false)
}
func (p *CompPoint) compPrefix() string {
pword := p.Words[p.CompWord]
if p.CompWordPos == 0 {
return ""
}
wordStr := p.wordAsStr(pword)
if p.CompWordPos == len(wordStr) {
return p.simpleExpandWord(pword)
}
// TODO we can do better, if p.Word is not nil, we can look for which WordPart
// our pos is in. we can then do a normal word expand on the previous parts
// 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 SimpleExpandPartialWord(shexec.SimpleExpandContext{}, partialWordStr, false)
}
func (p *CompPoint) dump() { func (p *CompPoint) dump() {
if p.Prefix != "" { if p.Prefix != "" {
fmt.Printf("prefix: %s\n", p.Prefix) fmt.Printf("prefix: %s\n", p.Prefix)
@ -55,9 +96,9 @@ func (p *CompPoint) dump() {
fmt.Printf("{%s}", w.Prefix) fmt.Printf("{%s}", w.Prefix)
} }
if idx == p.CompWord { if idx == p.CompWord {
fmt.Printf("%s\n", strWithCursor(w.Word, p.CompWordPos)) fmt.Printf("%s\n", strWithCursor(p.wordAsStr(w), p.CompWordPos))
} else { } else {
fmt.Printf("%s\n", w.Word) fmt.Printf("%s\n", p.wordAsStr(w))
} }
} }
if p.Suffix != "" { if p.Suffix != "" {
@ -66,8 +107,10 @@ func (p *CompPoint) dump() {
fmt.Printf("\n") fmt.Printf("\n")
} }
// directories will have a trailing "/"
type CompEntry struct { type CompEntry struct {
Word string Word string
IsMetaCmd bool
} }
type CompReturn struct { type CompReturn struct {
@ -154,6 +197,7 @@ func ParseCompPoint(fullCmdStr string, pos int) (*CompPoint, error) {
// fmt.Printf("found: ((%s))%s((%s))\n", rtnPoint.Prefix, strWithCursor(stmtStr, stmtPos), rtnPoint.Suffix) // fmt.Printf("found: ((%s))%s((%s))\n", rtnPoint.Prefix, strWithCursor(stmtStr, stmtPos), rtnPoint.Suffix)
// now, find the word that the pos appears in within the stmt above // now, find the word that the pos appears in within the stmt above
rtnPoint.StmtStr = stmtStr
stmtReader := strings.NewReader(stmtStr) stmtReader := strings.NewReader(stmtStr)
lastWordPos := 0 lastWordPos := 0
parser.Words(stmtReader, func(w *syntax.Word) bool { parser.Words(stmtReader, func(w *syntax.Word) bool {
@ -162,21 +206,22 @@ func ParseCompPoint(fullCmdStr string, pos int) (*CompPoint, error) {
if int(w.Pos().Offset()) > lastWordPos { if int(w.Pos().Offset()) > lastWordPos {
pword.Prefix = stmtStr[lastWordPos:w.Pos().Offset()] pword.Prefix = stmtStr[lastWordPos:w.Pos().Offset()]
} }
pword.Word = stmtStr[w.Pos().Offset():w.End().Offset()] pword.Word = w
rtnPoint.Words = append(rtnPoint.Words, pword) rtnPoint.Words = append(rtnPoint.Words, pword)
lastWordPos = int(w.End().Offset()) lastWordPos = int(w.End().Offset())
return true return true
}) })
if lastWordPos < len(stmtStr) { if lastWordPos < len(stmtStr) {
pword := ParsedWord{Offset: lastWordPos} pword := ParsedWord{Offset: lastWordPos}
pword.Prefix, pword.Word = splitInitialWhitespace(stmtStr[lastWordPos:]) pword.Prefix, pword.PartialWord = splitInitialWhitespace(stmtStr[lastWordPos:])
rtnPoint.Words = append(rtnPoint.Words, pword) rtnPoint.Words = append(rtnPoint.Words, pword)
} }
if len(rtnPoint.Words) == 0 { if len(rtnPoint.Words) == 0 {
rtnPoint.Words = append(rtnPoint.Words, ParsedWord{}) rtnPoint.Words = append(rtnPoint.Words, ParsedWord{})
} }
for idx, w := range rtnPoint.Words { for idx, w := range rtnPoint.Words {
if stmtPos > w.Offset && stmtPos <= w.Offset+len(w.Prefix)+len(w.Word) { wordLen := len(rtnPoint.wordAsStr(w))
if stmtPos > w.Offset && stmtPos <= w.Offset+len(w.Prefix)+wordLen {
rtnPoint.CompWord = idx rtnPoint.CompWord = idx
rtnPoint.CompWordPos = stmtPos - w.Offset - len(w.Prefix) rtnPoint.CompWordPos = stmtPos - w.Offset - len(w.Prefix)
if rtnPoint.CompWordPos < 0 { if rtnPoint.CompWordPos < 0 {
@ -184,7 +229,7 @@ func ParseCompPoint(fullCmdStr string, pos int) (*CompPoint, error) {
} }
} }
} }
// rtnPoint.dump() rtnPoint.dump()
return &rtnPoint, nil return &rtnPoint, nil
} }
@ -192,8 +237,8 @@ func splitCompWord(p *CompPoint) {
w := p.Words[p.CompWord] w := p.Words[p.CompWord]
prefixPos := p.CompWordPos + len(w.Prefix) prefixPos := p.CompWordPos + len(w.Prefix)
w1 := ParsedWord{Offset: w.Offset, Prefix: w.Prefix[:prefixPos], Word: ""} w1 := ParsedWord{Offset: w.Offset, Prefix: w.Prefix[:prefixPos]}
w2 := ParsedWord{Offset: w.Offset + prefixPos, Prefix: w.Prefix[prefixPos:], Word: w.Word} w2 := ParsedWord{Offset: w.Offset + prefixPos, Prefix: w.Prefix[prefixPos:], Word: w.Word, PartialWord: w.PartialWord}
p.CompWord = p.CompWord // the same (w1) p.CompWord = p.CompWord // the same (w1)
p.CompWordPos = 0 // will be at 0 since w1 has a word length of 0 p.CompWordPos = 0 // will be at 0 since w1 has a word length of 0
var newWords []ParsedWord var newWords []ParsedWord

54
pkg/comp/simplecomp.go Normal file
View File

@ -0,0 +1,54 @@
package comp
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/scripthaus-dev/mshell/pkg/packet"
"github.com/scripthaus-dev/sh2-server/pkg/remote"
)
func compsToCompReturn(comps []string, hasMore bool) *CompReturn {
var rtn CompReturn
rtn.HasMore = hasMore
for _, comp := range comps {
rtn.Entries = append(rtn.Entries, CompEntry{Word: comp})
}
return &rtn
}
func doCompGen(ctx context.Context, compCtx CompContext, prefix string, compType string, forDisplay bool) (*CompReturn, error) {
if !packet.IsValidCompGenType(compType) {
return nil, false, fmt.Errorf("/compgen invalid type '%s'", compType)
}
msh := remote.GetRemoteById(compCtx.RemotePtr.RemoteId)
if msh == nil {
return nil, false, fmt.Errorf("invalid remote '%s', not found", compCtx.RemotePtr)
}
cgPacket := packet.MakeCompGenPacket()
cgPacket.ReqId = uuid.New().String()
cgPacket.CompType = compType
cgPacket.Prefix = prefix
cgPacket.Cwd = compCtx.State.Cwd
resp, err := msh.PacketRpc(ctx, cgPacket)
if err != nil {
return nil, false, err
}
if err = resp.Err(); err != nil {
return nil, false, err
}
comps := getStrArr(resp.Data, "comps")
hasMore := getBool(resp.Data, "hasmore")
return compsToCompReturn(conmps, hasMore), nil
}
func SimpleCompFile(ctx context.Context, point SimpleCompPoint, compCtx CompContext, args []interface{}) (*CompReturn, error) {
pword := point.Words[p.CompWord]
prefix := ""
crtn, err := doCompGen(ctx, prefix, "file")
if err != nil {
return nil, err
}
return crtn, nil
}