From ea8c67dac9807b558b743951f9d85071a42b76ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Wed, 31 Jul 2024 23:10:20 +0100 Subject: [PATCH] more --- example.go | 76 +++++++++++++++++++++++++++++++++++++++++++ syntax/parser_test.go | 4 +++ syntax/walk.go | 4 +++ 3 files changed, 84 insertions(+) create mode 100644 example.go diff --git a/example.go b/example.go new file mode 100644 index 00000000..1c640eac --- /dev/null +++ b/example.go @@ -0,0 +1,76 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "os" + + "mvdan.cc/sh/v3/syntax" +) + +func main() { + src, err := io.ReadAll(os.Stdin) + if err != nil { + panic(err) + } + p := syntax.NewParser(syntax.RecoverErrors(5)) + f, err := p.Parse(bytes.NewReader(src), "") + if err != nil { + panic(err) + } + + last := lastStmt(f) + compl := extractCompletion(last, string(src)) + fmt.Printf("%#v\n", compl) +} + +func lastStmt(node syntax.Node) *syntax.Stmt { + var last *syntax.Stmt + // TODO: keep the first incomplete statement, too + syntax.Walk(node, func(node syntax.Node) bool { + if stmt, _ := node.(*syntax.Stmt); stmt != nil { + last = stmt + } + return true + }) + return last +} + +func extractCompletion(stmt *syntax.Stmt, src string) compResult { + syntax.DebugPrint(os.Stderr, stmt) + if len(stmt.Redirs) > 0 { + lastRedir := stmt.Redirs[len(stmt.Redirs)-1] + // fmt.Println(lastRedir.Word.Pos()) + // fmt.Fprintln(os.Stderr, lastRedir.Word.Pos()) + if lastRedir.Word.Pos().IsRecovered() { + return compResult{"redirect", nil} + } + } + if call, _ := stmt.Cmd.(*syntax.CallExpr); call != nil { + flat := flatWords(call.Args, src) + if stmt.End().Offset() < uint(len(src)) { + // Starting a new word. + flat = append(flat, "") + } + return compResult{"args", flat} + } + + // other commands, e.g. "if foo; then bar; fi" + return compResult{} +} + +func flatWords(words []*syntax.Word, src string) []string { + var flat []string + for _, word := range words { + start := word.Pos().Offset() + end := word.End().Offset() + flat = append(flat, src[start:end]) + } + return flat +} + +type compResult struct { + Type string + Tokens []string +} diff --git a/syntax/parser_test.go b/syntax/parser_test.go index 6cd097a1..ec7989de 100644 --- a/syntax/parser_test.go +++ b/syntax/parser_test.go @@ -2565,6 +2565,10 @@ func TestParseRecoverErrors(t *testing.T) { // src: "incomp >", // wantRecoveredPos: 1, // }, + { + src: "${incomp", + wantRecoveredPos: 1, + }, { src: "badsyntax)", wantErr: true, diff --git a/syntax/walk.go b/syntax/walk.go index 27fdcb1b..8cfff744 100644 --- a/syntax/walk.go +++ b/syntax/walk.go @@ -291,6 +291,10 @@ func (p *debugPrinter) print(x reflect.Value) { case reflect.Struct: if v, ok := x.Interface().(Pos); ok { + if v.IsRecovered() { + p.printf("") + return + } p.printf("%v:%v", v.Line(), v.Col()) return }