@wrongstack/tools 1.0.10 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin.js +17 -17
- package/dist/{chunk-6I6RXQFG.js → chunk-2BCRQAIW.js} +9 -9
- package/dist/{chunk-XRNN44QY.js → chunk-2IRHFHZY.js} +235 -71
- package/dist/{chunk-5RBUYFRV.js → chunk-4IMNVQRB.js} +8 -2
- package/dist/{chunk-NEYUPZL6.js → chunk-6D3UIQMF.js} +651 -303
- package/dist/chunk-BJ5LXLKE.js +351 -0
- package/dist/{chunk-C6ZOXNXV.js → chunk-CSS42GRP.js} +2 -2
- package/dist/{chunk-SKRMUHTU.js → chunk-HSLDY55L.js} +41 -8
- package/dist/{chunk-ENE22I5W.js → chunk-K37AKCTN.js} +884 -362
- package/dist/{chunk-V25Q6TQ7.js → chunk-K3VX6V5P.js} +3 -3
- package/dist/chunk-OIFY4XNP.js +855 -0
- package/dist/{chunk-OUERRFEM.js → chunk-OMQDKZOT.js} +17 -8
- package/dist/{chunk-7GOE2ZTZ.js → chunk-OMWC2OGT.js} +2 -2
- package/dist/{chunk-IE4KDAOB.js → chunk-OYQE2UNT.js} +3 -3
- package/dist/chunk-R3HR3QI3.js +97 -0
- package/dist/{chunk-XPVYZ2S6.js → chunk-RYLMSGR5.js} +2 -2
- package/dist/{chunk-HYHE4VNA.js → chunk-UMOP3VGK.js} +79 -45
- package/dist/{chunk-FVSMTEO3.js → chunk-VMNWQI4V.js} +2 -2
- package/dist/{chunk-2UVAIS5O.js → chunk-YPSVPCIE.js} +2 -2
- package/dist/{chunk-PWZUHZP7.js → chunk-Z77XMU3U.js} +2 -2
- package/dist/codebase-index/ast-invariant-engine.d.ts +19 -2
- package/dist/codebase-index/atlas-projection.d.ts +19 -0
- package/dist/codebase-index/background-indexer.d.ts +14 -0
- package/dist/codebase-index/circuit-breaker.d.ts +7 -0
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +6 -0
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +2 -0
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +26 -0
- package/dist/codebase-index/dead-code-scan.d.ts +4 -0
- package/dist/codebase-index/go-parser.d.ts +23 -3
- package/dist/codebase-index/graph-rank-pass.d.ts +10 -0
- package/dist/codebase-index/graph-rank.d.ts +8 -0
- package/dist/codebase-index/index.d.ts +1 -1
- package/dist/codebase-index/index.js +9 -9
- package/dist/codebase-index/lsp-kind.d.ts +3 -1
- package/dist/codebase-index/module-resolver.d.ts +21 -1
- package/dist/codebase-index/parser-batch.d.ts +6 -8
- package/dist/codebase-index/parser-output.d.ts +5 -1
- package/dist/codebase-index/parser-worker-script.js +4 -4
- package/dist/codebase-index/project-server-framing.d.ts +1 -17
- package/dist/codebase-index/project-server-watcher.d.ts +6 -0
- package/dist/codebase-index/project-server.js +72 -26
- package/dist/codebase-index/py-parser.d.ts +4 -4
- package/dist/codebase-index/rs-parser.d.ts +7 -0
- package/dist/codebase-index/toolchain-scripts.d.ts +64 -0
- package/dist/codebase-index/tree-sitter/queries.d.ts +14 -0
- package/dist/codebase-index/tree-sitter/util.d.ts +6 -1
- package/dist/codebase-index/tree-sitter-parser.d.ts +7 -0
- package/dist/codebase-index/worker.js +23 -16
- package/dist/codebase-index/writer-refs.d.ts +17 -2
- package/dist/codebase-index/writer-search-helpers.d.ts +9 -0
- package/dist/codebase-index/writer.d.ts +20 -1
- package/dist/edit.js +8 -8
- package/dist/{generic-parser-N2PLGBC2.js → generic-parser-IVWLFKMR.js} +2 -2
- package/dist/go-parser-LWV3L5V7.js +197 -0
- package/dist/index.js +19 -19
- package/dist/json-parser-PERV2JHX.js +204 -0
- package/dist/pack.js +18 -18
- package/dist/parser-dispatch-ZSWCQB6M.js +14 -0
- package/dist/patch.js +8 -8
- package/dist/{py-parser-4PFZYAJ7.js → py-parser-B6D6RGH7.js} +4 -4
- package/dist/read.js +8 -8
- package/dist/replace.js +8 -8
- package/dist/rs-parser-L7SP4GWJ.js +182 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +1 -1
- package/dist/tool-tier.js +19 -19
- package/dist/{tree-sitter-parser-GILYQLS4.js → tree-sitter-parser-KU6JF3ZC.js} +6 -4
- package/dist/{ts-parser-7W26YB26.js → ts-parser-PML6QIH7.js} +55 -23
- package/dist/typecheck.js +1 -1
- package/dist/write.js +8 -8
- package/dist/{yaml-parser-DFBC5FWH.js → yaml-parser-7BJN544B.js} +41 -6
- package/package.json +5 -5
- package/dist/chunk-4CE3RKEP.js +0 -390
- package/dist/chunk-KHUCKLOR.js +0 -899
- package/dist/chunk-ZKC5UNGD.js +0 -167
- package/dist/go-parser-Z4H6SR3R.js +0 -474
- package/dist/json-parser-IJD77VL4.js +0 -256
- package/dist/parser-dispatch-WG4DNBHH.js +0 -14
- package/dist/rs-parser-GIWPUA3T.js +0 -81
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The out-of-process parser programs (Go's `go/ast`, Python's `ast`) and the
|
|
3
|
+
* plumbing that runs them.
|
|
4
|
+
*
|
|
5
|
+
* The single-file parsers (`go-parser.ts`, `py-parser.ts`) and the batch
|
|
6
|
+
* parsers (`parser-batch.ts`) used to embed two hand-maintained copies of each
|
|
7
|
+
* program. The copies drifted: the Go batch reported 1-based columns while the
|
|
8
|
+
* single-file script reported 0-based ones, so the same file indexed with
|
|
9
|
+
* different positions depending on which path parsed it. Each language now has
|
|
10
|
+
* ONE extraction core; the single-file and batch programs differ only in their
|
|
11
|
+
* `main` (one source on stdin vs. a JSON array of sources).
|
|
12
|
+
*/
|
|
13
|
+
/** One Go source on stdin → `{"symbols": [...], "refs": [...]}`. */
|
|
14
|
+
export declare const GO_PARSE_SCRIPT = "\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Sym struct {\n\tName string `json:\"name\"`\n\tKind string `json:\"kind\"`\n\tLine int `json:\"line\"`\n\tCol int `json:\"col\"`\n\tSignature string `json:\"signature\"`\n\tScope string `json:\"scope\"`\n\tDoc string `json:\"doc\"`\n}\n\n// Ref is a cross-reference emitted alongside the symbols, so one run yields\n// both. Module is the import path for CallType \"import\", else empty.\ntype Ref struct {\n\tToName string `json:\"toName\"`\n\tCallType string `json:\"callType\"`\n\tLine int `json:\"line\"`\n\tModule string `json:\"module\"`\n}\n\nfunc extract(src []byte) ([]Sym, []Ref, error) {\n\tfset := token.NewFileSet()\n\tnode, err := parser.ParseFile(fset, \"src.go\", src, parser.ParseComments)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tpkg := node.Name.Name\n\t// token columns are 1-based; the index schema's are 0-based.\n\tat := func(p token.Pos) (int, int) {\n\t\tpos := fset.Position(p)\n\t\treturn pos.Line, pos.Column - 1\n\t}\n\n\tsyms := []Sym{}\n\tfor _, decl := range node.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tline, col := at(d.Pos())\n\t\t\tkind := \"function\"\n\t\t\tscope := pkg + \".\" + d.Name.Name\n\t\t\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\t\t\tkind = \"method\"\n\t\t\t\tscope = pkg + \".\" + recvTypeName(d.Recv.List[0].Type) + \".\" + d.Name.Name\n\t\t\t}\n\t\t\tsyms = append(syms, Sym{Name: d.Name.Name, Kind: kind, Line: line, Col: col, Signature: formatFuncSig(d), Scope: scope, Doc: firstDocLine(d.Doc)})\n\n\t\tcase *ast.GenDecl:\n\t\t\t// An ungrouped declaration carries its comment on the GenDecl; a\n\t\t\t// grouped one's doc describes the group, not each member.\n\t\t\tvar declDoc *ast.CommentGroup\n\t\t\tif !d.Lparen.IsValid() {\n\t\t\t\tdeclDoc = d.Doc\n\t\t\t}\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tswitch s := spec.(type) {\n\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\tline, col := at(s.Pos())\n\t\t\t\t\tsig := \"type \" + s.Name.Name + formatTypeParams(s.TypeParams)\n\t\t\t\t\tif s.Assign.IsValid() {\n\t\t\t\t\t\tsig += \" =\"\n\t\t\t\t\t}\n\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\tsyms = append(syms, Sym{Name: s.Name.Name, Kind: \"type\", Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\tkind := \"var\"\n\t\t\t\t\tif d.Tok == token.CONST {\n\t\t\t\t\t\tkind = \"const\"\n\t\t\t\t\t}\n\t\t\t\t\tfor _, n := range s.Names {\n\t\t\t\t\t\tline, col := at(n.Pos())\n\t\t\t\t\t\tsig := kind + \" \" + n.Name\n\t\t\t\t\t\tif s.Type != nil {\n\t\t\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsyms = append(syms, Sym{Name: n.Name, Kind: kind, Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\trefs := []Ref{}\n\tast.Inspect(node, func(n ast.Node) bool {\n\t\tswitch expr := n.(type) {\n\t\tcase *ast.CallExpr:\n\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\tswitch fun := expr.Fun.(type) {\n\t\t\tcase *ast.Ident:\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Name, CallType: \"call\", Line: line})\n\t\t\tcase *ast.SelectorExpr:\n\t\t\t\t// The selected name (`Join` of `filepath.Join`) is the declared\n\t\t\t\t// symbol name, so it resolves like the TS and Python call refs.\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Sel.Name, CallType: \"call\", Line: line})\n\t\t\t}\n\t\tcase *ast.ImportSpec:\n\t\t\tif expr.Path != nil {\n\t\t\t\tif importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {\n\t\t\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\t\t\t// A Go import names a package; its last path segment is the\n\t\t\t\t\t// name it is referenced by.\n\t\t\t\t\tname := importPath\n\t\t\t\t\tif idx := strings.LastIndex(importPath, \"/\"); idx >= 0 {\n\t\t\t\t\t\tname = importPath[idx+1:]\n\t\t\t\t\t}\n\t\t\t\t\trefs = append(refs, Ref{ToName: name, CallType: \"import\", Line: line, Module: importPath})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn syms, refs, nil\n}\n\nfunc firstDocLine(groups ...*ast.CommentGroup) string {\n\tfor _, g := range groups {\n\t\tif g == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, line := range strings.Split(g.Text(), \"\\n\") {\n\t\t\tif t := strings.TrimSpace(line); t != \"\" {\n\t\t\t\tif r := []rune(t); len(r) > 200 {\n\t\t\t\t\tt = string(r[:200])\n\t\t\t\t}\n\t\t\t\treturn t\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// recvTypeName unwraps pointers, parens and generic instantiation:\n// `(l *List[T])` is a method of List, not of \"?\".\nfunc recvTypeName(t ast.Expr) string {\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.StarExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexListExpr:\n\t\treturn recvTypeName(v.X)\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\nfunc formatFuncSig(d *ast.FuncDecl) string {\n\tsig := \"func \"\n\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\tsig += formatFieldList(d.Recv.List) + \" \"\n\t}\n\treturn sig + d.Name.Name + formatTypeParams(d.Type.TypeParams) + formatFuncType(d.Type)\n}\n\nfunc fieldsOf(fl *ast.FieldList) []*ast.Field {\n\tif fl == nil {\n\t\treturn nil\n\t}\n\treturn fl.List\n}\n\nfunc formatFuncType(f *ast.FuncType) string {\n\tout := formatFieldList(fieldsOf(f.Params))\n\tresults := fieldsOf(f.Results)\n\tif len(results) == 1 && len(results[0].Names) == 0 {\n\t\tout += \" \" + formatType(results[0].Type)\n\t} else if len(results) > 0 {\n\t\tout += \" \" + formatFieldList(results)\n\t}\n\treturn out\n}\n\n// formatField keeps every name of a shared-type field: `a, b int`.\nfunc formatField(f *ast.Field) string {\n\tt := formatType(f.Type)\n\tif len(f.Names) == 0 {\n\t\treturn t\n\t}\n\tnames := make([]string, len(f.Names))\n\tfor i, n := range f.Names {\n\t\tnames[i] = n.Name\n\t}\n\treturn strings.Join(names, \", \") + \" \" + t\n}\n\nfunc formatFieldList(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tparts[i] = formatField(f)\n\t}\n\treturn \"(\" + strings.Join(parts, \", \") + \")\"\n}\n\n// formatFields renders struct fields and interface members.\nfunc formatFields(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tif ft, ok := f.Type.(*ast.FuncType); ok && len(f.Names) == 1 {\n\t\t\tparts[i] = f.Names[0].Name + formatFuncType(ft)\n\t\t} else {\n\t\t\tparts[i] = formatField(f)\n\t\t}\n\t}\n\treturn strings.Join(parts, \"; \")\n}\n\nfunc formatTypeParams(tp *ast.FieldList) string {\n\tif tp == nil || len(tp.List) == 0 {\n\t\treturn \"\"\n\t}\n\tparts := make([]string, len(tp.List))\n\tfor i, p := range tp.List {\n\t\tparts[i] = formatField(p)\n\t}\n\treturn \"[\" + strings.Join(parts, \", \") + \"]\"\n}\n\nfunc formatType(t ast.Expr) string {\n\tif t == nil {\n\t\treturn \"?\"\n\t}\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.SelectorExpr:\n\t\treturn formatType(v.X) + \".\" + v.Sel.Name\n\tcase *ast.StarExpr:\n\t\treturn \"*\" + formatType(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn \"(\" + formatType(v.X) + \")\"\n\tcase *ast.Ellipsis:\n\t\tif v.Elt == nil {\n\t\t\treturn \"...\"\n\t\t}\n\t\treturn \"...\" + formatType(v.Elt)\n\tcase *ast.ArrayType:\n\t\tif v.Len == nil {\n\t\t\treturn \"[]\" + formatType(v.Elt)\n\t\t}\n\t\treturn \"[\" + formatType(v.Len) + \"]\" + formatType(v.Elt)\n\tcase *ast.MapType:\n\t\treturn \"map[\" + formatType(v.Key) + \"]\" + formatType(v.Value)\n\tcase *ast.ChanType:\n\t\tswitch v.Dir {\n\t\tcase ast.SEND:\n\t\t\treturn \"chan<- \" + formatType(v.Value)\n\t\tcase ast.RECV:\n\t\t\treturn \"<-chan \" + formatType(v.Value)\n\t\tdefault:\n\t\t\treturn \"chan \" + formatType(v.Value)\n\t\t}\n\tcase *ast.InterfaceType:\n\t\tif len(fieldsOf(v.Methods)) == 0 {\n\t\t\treturn \"interface{}\"\n\t\t}\n\t\treturn \"interface{ \" + formatFields(v.Methods.List) + \" }\"\n\tcase *ast.StructType:\n\t\tif len(fieldsOf(v.Fields)) == 0 {\n\t\t\treturn \"struct{}\"\n\t\t}\n\t\treturn \"struct{ \" + formatFields(v.Fields.List) + \" }\"\n\tcase *ast.FuncType:\n\t\treturn \"func\" + formatFuncType(v)\n\tcase *ast.UnaryExpr:\n\t\treturn v.Op.String() + formatType(v.X)\n\tcase *ast.BinaryExpr:\n\t\treturn formatType(v.X) + \" \" + v.Op.String() + \" \" + formatType(v.Y)\n\tcase *ast.BasicLit:\n\t\treturn v.Value\n\tcase *ast.IndexExpr:\n\t\treturn formatType(v.X) + \"[\" + formatType(v.Index) + \"]\"\n\tcase *ast.IndexListExpr:\n\t\targs := make([]string, len(v.Indices))\n\t\tfor i, idx := range v.Indices {\n\t\t\targs[i] = formatType(idx)\n\t\t}\n\t\treturn formatType(v.X) + \"[\" + strings.Join(args, \", \") + \"]\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\ntype Result struct {\n\tSymbols []Sym `json:\"symbols\"`\n\tRefs []Ref `json:\"refs\"`\n}\n\nfunc main() {\n\tempty := `{\"symbols\":[],\"refs\":[]}`\n\tsrc, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\tfmt.Print(empty)\n\t\treturn\n\t}\n\tsyms, refs, err := extract(src)\n\tif err != nil {\n\t\tfmt.Print(empty)\n\t\treturn\n\t}\n\tdata, err := json.Marshal(Result{Symbols: syms, Refs: refs})\n\tif err != nil {\n\t\tfmt.Print(empty)\n\t\treturn\n\t}\n\tfmt.Print(string(data))\n}\n";
|
|
15
|
+
/** A JSON array of `{file, content}` on stdin → one envelope with per-file results. */
|
|
16
|
+
export declare const GO_BATCH_SCRIPT = "\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Sym struct {\n\tName string `json:\"name\"`\n\tKind string `json:\"kind\"`\n\tLine int `json:\"line\"`\n\tCol int `json:\"col\"`\n\tSignature string `json:\"signature\"`\n\tScope string `json:\"scope\"`\n\tDoc string `json:\"doc\"`\n}\n\n// Ref is a cross-reference emitted alongside the symbols, so one run yields\n// both. Module is the import path for CallType \"import\", else empty.\ntype Ref struct {\n\tToName string `json:\"toName\"`\n\tCallType string `json:\"callType\"`\n\tLine int `json:\"line\"`\n\tModule string `json:\"module\"`\n}\n\nfunc extract(src []byte) ([]Sym, []Ref, error) {\n\tfset := token.NewFileSet()\n\tnode, err := parser.ParseFile(fset, \"src.go\", src, parser.ParseComments)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tpkg := node.Name.Name\n\t// token columns are 1-based; the index schema's are 0-based.\n\tat := func(p token.Pos) (int, int) {\n\t\tpos := fset.Position(p)\n\t\treturn pos.Line, pos.Column - 1\n\t}\n\n\tsyms := []Sym{}\n\tfor _, decl := range node.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tline, col := at(d.Pos())\n\t\t\tkind := \"function\"\n\t\t\tscope := pkg + \".\" + d.Name.Name\n\t\t\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\t\t\tkind = \"method\"\n\t\t\t\tscope = pkg + \".\" + recvTypeName(d.Recv.List[0].Type) + \".\" + d.Name.Name\n\t\t\t}\n\t\t\tsyms = append(syms, Sym{Name: d.Name.Name, Kind: kind, Line: line, Col: col, Signature: formatFuncSig(d), Scope: scope, Doc: firstDocLine(d.Doc)})\n\n\t\tcase *ast.GenDecl:\n\t\t\t// An ungrouped declaration carries its comment on the GenDecl; a\n\t\t\t// grouped one's doc describes the group, not each member.\n\t\t\tvar declDoc *ast.CommentGroup\n\t\t\tif !d.Lparen.IsValid() {\n\t\t\t\tdeclDoc = d.Doc\n\t\t\t}\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tswitch s := spec.(type) {\n\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\tline, col := at(s.Pos())\n\t\t\t\t\tsig := \"type \" + s.Name.Name + formatTypeParams(s.TypeParams)\n\t\t\t\t\tif s.Assign.IsValid() {\n\t\t\t\t\t\tsig += \" =\"\n\t\t\t\t\t}\n\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\tsyms = append(syms, Sym{Name: s.Name.Name, Kind: \"type\", Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\tkind := \"var\"\n\t\t\t\t\tif d.Tok == token.CONST {\n\t\t\t\t\t\tkind = \"const\"\n\t\t\t\t\t}\n\t\t\t\t\tfor _, n := range s.Names {\n\t\t\t\t\t\tline, col := at(n.Pos())\n\t\t\t\t\t\tsig := kind + \" \" + n.Name\n\t\t\t\t\t\tif s.Type != nil {\n\t\t\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsyms = append(syms, Sym{Name: n.Name, Kind: kind, Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\trefs := []Ref{}\n\tast.Inspect(node, func(n ast.Node) bool {\n\t\tswitch expr := n.(type) {\n\t\tcase *ast.CallExpr:\n\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\tswitch fun := expr.Fun.(type) {\n\t\t\tcase *ast.Ident:\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Name, CallType: \"call\", Line: line})\n\t\t\tcase *ast.SelectorExpr:\n\t\t\t\t// The selected name (`Join` of `filepath.Join`) is the declared\n\t\t\t\t// symbol name, so it resolves like the TS and Python call refs.\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Sel.Name, CallType: \"call\", Line: line})\n\t\t\t}\n\t\tcase *ast.ImportSpec:\n\t\t\tif expr.Path != nil {\n\t\t\t\tif importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {\n\t\t\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\t\t\t// A Go import names a package; its last path segment is the\n\t\t\t\t\t// name it is referenced by.\n\t\t\t\t\tname := importPath\n\t\t\t\t\tif idx := strings.LastIndex(importPath, \"/\"); idx >= 0 {\n\t\t\t\t\t\tname = importPath[idx+1:]\n\t\t\t\t\t}\n\t\t\t\t\trefs = append(refs, Ref{ToName: name, CallType: \"import\", Line: line, Module: importPath})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn syms, refs, nil\n}\n\nfunc firstDocLine(groups ...*ast.CommentGroup) string {\n\tfor _, g := range groups {\n\t\tif g == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, line := range strings.Split(g.Text(), \"\\n\") {\n\t\t\tif t := strings.TrimSpace(line); t != \"\" {\n\t\t\t\tif r := []rune(t); len(r) > 200 {\n\t\t\t\t\tt = string(r[:200])\n\t\t\t\t}\n\t\t\t\treturn t\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// recvTypeName unwraps pointers, parens and generic instantiation:\n// `(l *List[T])` is a method of List, not of \"?\".\nfunc recvTypeName(t ast.Expr) string {\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.StarExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexListExpr:\n\t\treturn recvTypeName(v.X)\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\nfunc formatFuncSig(d *ast.FuncDecl) string {\n\tsig := \"func \"\n\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\tsig += formatFieldList(d.Recv.List) + \" \"\n\t}\n\treturn sig + d.Name.Name + formatTypeParams(d.Type.TypeParams) + formatFuncType(d.Type)\n}\n\nfunc fieldsOf(fl *ast.FieldList) []*ast.Field {\n\tif fl == nil {\n\t\treturn nil\n\t}\n\treturn fl.List\n}\n\nfunc formatFuncType(f *ast.FuncType) string {\n\tout := formatFieldList(fieldsOf(f.Params))\n\tresults := fieldsOf(f.Results)\n\tif len(results) == 1 && len(results[0].Names) == 0 {\n\t\tout += \" \" + formatType(results[0].Type)\n\t} else if len(results) > 0 {\n\t\tout += \" \" + formatFieldList(results)\n\t}\n\treturn out\n}\n\n// formatField keeps every name of a shared-type field: `a, b int`.\nfunc formatField(f *ast.Field) string {\n\tt := formatType(f.Type)\n\tif len(f.Names) == 0 {\n\t\treturn t\n\t}\n\tnames := make([]string, len(f.Names))\n\tfor i, n := range f.Names {\n\t\tnames[i] = n.Name\n\t}\n\treturn strings.Join(names, \", \") + \" \" + t\n}\n\nfunc formatFieldList(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tparts[i] = formatField(f)\n\t}\n\treturn \"(\" + strings.Join(parts, \", \") + \")\"\n}\n\n// formatFields renders struct fields and interface members.\nfunc formatFields(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tif ft, ok := f.Type.(*ast.FuncType); ok && len(f.Names) == 1 {\n\t\t\tparts[i] = f.Names[0].Name + formatFuncType(ft)\n\t\t} else {\n\t\t\tparts[i] = formatField(f)\n\t\t}\n\t}\n\treturn strings.Join(parts, \"; \")\n}\n\nfunc formatTypeParams(tp *ast.FieldList) string {\n\tif tp == nil || len(tp.List) == 0 {\n\t\treturn \"\"\n\t}\n\tparts := make([]string, len(tp.List))\n\tfor i, p := range tp.List {\n\t\tparts[i] = formatField(p)\n\t}\n\treturn \"[\" + strings.Join(parts, \", \") + \"]\"\n}\n\nfunc formatType(t ast.Expr) string {\n\tif t == nil {\n\t\treturn \"?\"\n\t}\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.SelectorExpr:\n\t\treturn formatType(v.X) + \".\" + v.Sel.Name\n\tcase *ast.StarExpr:\n\t\treturn \"*\" + formatType(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn \"(\" + formatType(v.X) + \")\"\n\tcase *ast.Ellipsis:\n\t\tif v.Elt == nil {\n\t\t\treturn \"...\"\n\t\t}\n\t\treturn \"...\" + formatType(v.Elt)\n\tcase *ast.ArrayType:\n\t\tif v.Len == nil {\n\t\t\treturn \"[]\" + formatType(v.Elt)\n\t\t}\n\t\treturn \"[\" + formatType(v.Len) + \"]\" + formatType(v.Elt)\n\tcase *ast.MapType:\n\t\treturn \"map[\" + formatType(v.Key) + \"]\" + formatType(v.Value)\n\tcase *ast.ChanType:\n\t\tswitch v.Dir {\n\t\tcase ast.SEND:\n\t\t\treturn \"chan<- \" + formatType(v.Value)\n\t\tcase ast.RECV:\n\t\t\treturn \"<-chan \" + formatType(v.Value)\n\t\tdefault:\n\t\t\treturn \"chan \" + formatType(v.Value)\n\t\t}\n\tcase *ast.InterfaceType:\n\t\tif len(fieldsOf(v.Methods)) == 0 {\n\t\t\treturn \"interface{}\"\n\t\t}\n\t\treturn \"interface{ \" + formatFields(v.Methods.List) + \" }\"\n\tcase *ast.StructType:\n\t\tif len(fieldsOf(v.Fields)) == 0 {\n\t\t\treturn \"struct{}\"\n\t\t}\n\t\treturn \"struct{ \" + formatFields(v.Fields.List) + \" }\"\n\tcase *ast.FuncType:\n\t\treturn \"func\" + formatFuncType(v)\n\tcase *ast.UnaryExpr:\n\t\treturn v.Op.String() + formatType(v.X)\n\tcase *ast.BinaryExpr:\n\t\treturn formatType(v.X) + \" \" + v.Op.String() + \" \" + formatType(v.Y)\n\tcase *ast.BasicLit:\n\t\treturn v.Value\n\tcase *ast.IndexExpr:\n\t\treturn formatType(v.X) + \"[\" + formatType(v.Index) + \"]\"\n\tcase *ast.IndexListExpr:\n\t\targs := make([]string, len(v.Indices))\n\t\tfor i, idx := range v.Indices {\n\t\t\targs[i] = formatType(idx)\n\t\t}\n\t\treturn formatType(v.X) + \"[\" + strings.Join(args, \", \") + \"]\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\ntype FileResult struct {\n\tFile string `json:\"file\"`\n\tError string `json:\"error,omitempty\"`\n\tSymbols []Sym `json:\"symbols\"`\n\tRefs []Ref `json:\"refs\"`\n}\n\ntype BatchResult struct {\n\tVersion int `json:\"version\"`\n\tResults []FileResult `json:\"results\"`\n}\n\ntype inputFile struct {\n\tFile string `json:\"file\"`\n\tContent string `json:\"content\"`\n}\n\nfunc main() {\n\tvar inputs []inputFile\n\traw, err := io.ReadAll(os.Stdin)\n\tif err == nil {\n\t\terr = json.Unmarshal(raw, &inputs)\n\t}\n\tresults := make([]FileResult, 0, len(inputs))\n\tif err == nil {\n\t\tfor _, in := range inputs {\n\t\t\tres := FileResult{File: in.File, Symbols: []Sym{}, Refs: []Ref{}}\n\t\t\tif syms, refs, perr := extract([]byte(in.Content)); perr != nil {\n\t\t\t\tres.Error = perr.Error()\n\t\t\t} else {\n\t\t\t\tres.Symbols, res.Refs = syms, refs\n\t\t\t}\n\t\t\tresults = append(results, res)\n\t\t}\n\t}\n\tdata, merr := json.Marshal(BatchResult{Version: 1, Results: results})\n\tif merr != nil {\n\t\tfmt.Print(`{\"version\":1,\"results\":[]}`)\n\t\treturn\n\t}\n\tfmt.Print(string(data))\n}\n";
|
|
17
|
+
/** One Python source on stdin (file path in argv[1]) → `{"symbols", "refs"}`. */
|
|
18
|
+
export declare const PY_PARSE_SCRIPT = "import ast, json, sys\n\n# Decorators whose function is an attribute accessor, by last dotted segment.\nPROPERTY_DECORATORS = (\"property\", \"cached_property\", \"abstractproperty\", \"setter\", \"getter\", \"deleter\")\n\ndef get_name(node):\n if isinstance(node, ast.Name):\n return node.id\n if isinstance(node, ast.Attribute):\n return get_name(node.value) + \".\" + node.attr\n if isinstance(node, ast.Subscript):\n return get_name(node.value)\n if isinstance(node, ast.Call):\n return get_name(node.func)\n if isinstance(node, ast.Constant):\n return str(node.value)\n return \"\"\n\ndef leaf_name(node):\n # Declared name of the callee: `join` of `os.path.join`. Matches how the\n # TypeScript and Go extractors record call refs.\n if isinstance(node, ast.Attribute):\n return node.attr\n if isinstance(node, ast.Name):\n return node.id\n return get_name(node).split(\".\")[-1]\n\ndef is_private(name):\n return name.startswith(\"__\") and not name.endswith(\"__\")\n\ndef first_doc_line(node):\n try:\n doc = ast.get_docstring(node)\n except Exception:\n return \"\"\n for line in (doc or \"\").splitlines():\n line = line.strip()\n if line:\n return line[:200]\n return \"\"\n\ndef module_name_of(path):\n base = path.replace(\"\\\\\", \"/\").rsplit(\"/\", 1)[-1]\n return base.rsplit(\".\", 1)[0] if \".\" in base else base\n\ndef read_stdin_text():\n # Decode explicitly: sys.stdin uses the locale code page on Windows\n # (cp1254, cp1252, ...), which mangles or rejects the UTF-8 we are sent.\n return sys.stdin.buffer.read().decode(\"utf-8\", \"replace\")\n\ndef parse_source(text, name):\n # ast.parse rejects a leading U+FEFF in a str source.\n if text.startswith(\"\\ufeff\"):\n text = text[1:]\n return ast.parse(text, filename=name or \"<module>\")\n\ndef format_args(args):\n parts = [a.arg for a in list(getattr(args, \"posonlyargs\", [])) + list(args.args)]\n if args.vararg is not None:\n parts.append(\"*\" + args.vararg.arg)\n elif args.kwonlyargs:\n parts.append(\"*\")\n parts.extend(a.arg for a in args.kwonlyargs)\n if args.kwarg is not None:\n parts.append(\"**\" + args.kwarg.arg)\n return \", \".join(parts)\n\ndef extract(tree, module_name):\n syms = []\n refs = []\n scope = [module_name]\n\n def add(name, kind, line, col, signature, sym_scope, doc=\"\"):\n syms.append({\n \"name\": name, \"kind\": kind, \"line\": line, \"col\": col,\n \"signature\": signature, \"scope\": sym_scope, \"doc\": doc,\n })\n\n def qualified(name):\n return \".\".join(scope) + \".\" + name\n\n # Kinds are the index schema's (class/function/method/property/var/const/\n # type). Imports are refs, not symbols: an import symbol per file made\n # every imported name look like a definition in every importing module.\n class Visitor(ast.NodeVisitor):\n in_class = False\n\n def visit_ClassDef(self, node):\n bases = [get_name(b) for b in node.bases]\n sig = \"class \" + node.name + (\"(\" + \", \".join(bases) + \")\" if bases else \"\") + \": ...\"\n add(node.name, \"class\", node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n scope.append(node.name)\n outer = self.in_class\n self.in_class = True\n try:\n self.generic_visit(node)\n finally:\n self.in_class = outer\n scope.pop()\n\n def visit_FunctionDef(self, node):\n kind = \"method\" if self.in_class else \"function\"\n for dec in node.decorator_list:\n if get_name(dec).rsplit(\".\", 1)[-1] in PROPERTY_DECORATORS:\n kind = \"property\"\n prefix = \"async def \" if isinstance(node, ast.AsyncFunctionDef) else \"def \"\n sig = prefix + node.name + \"(\" + format_args(node.args) + \")\"\n returns = get_name(node.returns) if node.returns is not None else \"\"\n if returns:\n sig += \" -> \" + returns\n add(node.name, kind, node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n # Bodies are not visited: function locals are not index symbols.\n\n visit_AsyncFunctionDef = visit_FunctionDef\n\n def add_target(self, target, line, annotation=None, has_value=True):\n if isinstance(target, ast.Name):\n if is_private(target.id):\n return\n kind = \"const\" if target.id.isupper() else \"var\"\n if annotation is not None:\n sig = target.id + \": \" + get_name(annotation) + (\" = ...\" if has_value else \"\")\n else:\n sig = target.id + \" = ...\"\n add(target.id, kind, line, target.col_offset, sig, \".\".join(scope))\n elif isinstance(target, (ast.Tuple, ast.List)):\n for elt in target.elts:\n self.add_target(elt, line)\n elif isinstance(target, ast.Starred):\n self.add_target(target.value, line)\n\n def visit_Assign(self, node):\n for target in node.targets:\n self.add_target(target, node.lineno)\n\n def visit_AnnAssign(self, node):\n self.add_target(node.target, node.lineno, node.annotation, node.value is not None)\n\n def visit_TypeAlias(self, node):\n if isinstance(node.name, ast.Name):\n add(node.name.id, \"type\", node.lineno, node.col_offset, \"type \" + node.name.id + \" = ...\", \".\".join(scope))\n\n Visitor().visit(tree)\n\n # Refs need a full walk: the visitor deliberately skips function bodies,\n # which is exactly where the calls are.\n for node in ast.walk(tree):\n if isinstance(node, ast.Call):\n name = leaf_name(node.func)\n if name:\n refs.append({\"toName\": name, \"callType\": \"call\", \"line\": node.lineno})\n elif isinstance(node, ast.Import):\n for alias in node.names:\n refs.append({\n \"toName\": alias.name.split(\".\")[-1], \"callType\": \"import\",\n \"line\": node.lineno, \"module\": alias.name,\n })\n elif isinstance(node, ast.ImportFrom):\n # PEP 328: keep the leading dots so the resolver can walk up from\n # the importing file's package.\n module = (\".\" * (node.level or 0)) + (node.module or \"\")\n for alias in node.names:\n refs.append({\n \"toName\": alias.name, \"callType\": \"import\",\n \"line\": node.lineno, \"module\": module,\n })\n elif isinstance(node, ast.ClassDef):\n for base in node.bases:\n name = leaf_name(base)\n if name:\n refs.append({\"toName\": name, \"callType\": \"inherit\", \"line\": node.lineno})\n\n return syms, refs\n\ndef main():\n name = sys.argv[1] if len(sys.argv) > 1 else \"module.py\"\n try:\n syms, refs = extract(parse_source(read_stdin_text(), name), module_name_of(name))\n except Exception:\n syms, refs = [], []\n print(json.dumps({\"symbols\": syms, \"refs\": refs}))\n\nmain()\n";
|
|
19
|
+
/** A JSON array of `{file, content}` on stdin → one envelope with per-file results. */
|
|
20
|
+
export declare const PY_BATCH_SCRIPT = "import ast, json, sys\n\n# Decorators whose function is an attribute accessor, by last dotted segment.\nPROPERTY_DECORATORS = (\"property\", \"cached_property\", \"abstractproperty\", \"setter\", \"getter\", \"deleter\")\n\ndef get_name(node):\n if isinstance(node, ast.Name):\n return node.id\n if isinstance(node, ast.Attribute):\n return get_name(node.value) + \".\" + node.attr\n if isinstance(node, ast.Subscript):\n return get_name(node.value)\n if isinstance(node, ast.Call):\n return get_name(node.func)\n if isinstance(node, ast.Constant):\n return str(node.value)\n return \"\"\n\ndef leaf_name(node):\n # Declared name of the callee: `join` of `os.path.join`. Matches how the\n # TypeScript and Go extractors record call refs.\n if isinstance(node, ast.Attribute):\n return node.attr\n if isinstance(node, ast.Name):\n return node.id\n return get_name(node).split(\".\")[-1]\n\ndef is_private(name):\n return name.startswith(\"__\") and not name.endswith(\"__\")\n\ndef first_doc_line(node):\n try:\n doc = ast.get_docstring(node)\n except Exception:\n return \"\"\n for line in (doc or \"\").splitlines():\n line = line.strip()\n if line:\n return line[:200]\n return \"\"\n\ndef module_name_of(path):\n base = path.replace(\"\\\\\", \"/\").rsplit(\"/\", 1)[-1]\n return base.rsplit(\".\", 1)[0] if \".\" in base else base\n\ndef read_stdin_text():\n # Decode explicitly: sys.stdin uses the locale code page on Windows\n # (cp1254, cp1252, ...), which mangles or rejects the UTF-8 we are sent.\n return sys.stdin.buffer.read().decode(\"utf-8\", \"replace\")\n\ndef parse_source(text, name):\n # ast.parse rejects a leading U+FEFF in a str source.\n if text.startswith(\"\\ufeff\"):\n text = text[1:]\n return ast.parse(text, filename=name or \"<module>\")\n\ndef format_args(args):\n parts = [a.arg for a in list(getattr(args, \"posonlyargs\", [])) + list(args.args)]\n if args.vararg is not None:\n parts.append(\"*\" + args.vararg.arg)\n elif args.kwonlyargs:\n parts.append(\"*\")\n parts.extend(a.arg for a in args.kwonlyargs)\n if args.kwarg is not None:\n parts.append(\"**\" + args.kwarg.arg)\n return \", \".join(parts)\n\ndef extract(tree, module_name):\n syms = []\n refs = []\n scope = [module_name]\n\n def add(name, kind, line, col, signature, sym_scope, doc=\"\"):\n syms.append({\n \"name\": name, \"kind\": kind, \"line\": line, \"col\": col,\n \"signature\": signature, \"scope\": sym_scope, \"doc\": doc,\n })\n\n def qualified(name):\n return \".\".join(scope) + \".\" + name\n\n # Kinds are the index schema's (class/function/method/property/var/const/\n # type). Imports are refs, not symbols: an import symbol per file made\n # every imported name look like a definition in every importing module.\n class Visitor(ast.NodeVisitor):\n in_class = False\n\n def visit_ClassDef(self, node):\n bases = [get_name(b) for b in node.bases]\n sig = \"class \" + node.name + (\"(\" + \", \".join(bases) + \")\" if bases else \"\") + \": ...\"\n add(node.name, \"class\", node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n scope.append(node.name)\n outer = self.in_class\n self.in_class = True\n try:\n self.generic_visit(node)\n finally:\n self.in_class = outer\n scope.pop()\n\n def visit_FunctionDef(self, node):\n kind = \"method\" if self.in_class else \"function\"\n for dec in node.decorator_list:\n if get_name(dec).rsplit(\".\", 1)[-1] in PROPERTY_DECORATORS:\n kind = \"property\"\n prefix = \"async def \" if isinstance(node, ast.AsyncFunctionDef) else \"def \"\n sig = prefix + node.name + \"(\" + format_args(node.args) + \")\"\n returns = get_name(node.returns) if node.returns is not None else \"\"\n if returns:\n sig += \" -> \" + returns\n add(node.name, kind, node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n # Bodies are not visited: function locals are not index symbols.\n\n visit_AsyncFunctionDef = visit_FunctionDef\n\n def add_target(self, target, line, annotation=None, has_value=True):\n if isinstance(target, ast.Name):\n if is_private(target.id):\n return\n kind = \"const\" if target.id.isupper() else \"var\"\n if annotation is not None:\n sig = target.id + \": \" + get_name(annotation) + (\" = ...\" if has_value else \"\")\n else:\n sig = target.id + \" = ...\"\n add(target.id, kind, line, target.col_offset, sig, \".\".join(scope))\n elif isinstance(target, (ast.Tuple, ast.List)):\n for elt in target.elts:\n self.add_target(elt, line)\n elif isinstance(target, ast.Starred):\n self.add_target(target.value, line)\n\n def visit_Assign(self, node):\n for target in node.targets:\n self.add_target(target, node.lineno)\n\n def visit_AnnAssign(self, node):\n self.add_target(node.target, node.lineno, node.annotation, node.value is not None)\n\n def visit_TypeAlias(self, node):\n if isinstance(node.name, ast.Name):\n add(node.name.id, \"type\", node.lineno, node.col_offset, \"type \" + node.name.id + \" = ...\", \".\".join(scope))\n\n Visitor().visit(tree)\n\n # Refs need a full walk: the visitor deliberately skips function bodies,\n # which is exactly where the calls are.\n for node in ast.walk(tree):\n if isinstance(node, ast.Call):\n name = leaf_name(node.func)\n if name:\n refs.append({\"toName\": name, \"callType\": \"call\", \"line\": node.lineno})\n elif isinstance(node, ast.Import):\n for alias in node.names:\n refs.append({\n \"toName\": alias.name.split(\".\")[-1], \"callType\": \"import\",\n \"line\": node.lineno, \"module\": alias.name,\n })\n elif isinstance(node, ast.ImportFrom):\n # PEP 328: keep the leading dots so the resolver can walk up from\n # the importing file's package.\n module = (\".\" * (node.level or 0)) + (node.module or \"\")\n for alias in node.names:\n refs.append({\n \"toName\": alias.name, \"callType\": \"import\",\n \"line\": node.lineno, \"module\": module,\n })\n elif isinstance(node, ast.ClassDef):\n for base in node.bases:\n name = leaf_name(base)\n if name:\n refs.append({\"toName\": name, \"callType\": \"inherit\", \"line\": node.lineno})\n\n return syms, refs\n\ndef main():\n try:\n inputs = json.loads(read_stdin_text())\n except Exception:\n print(json.dumps({\"version\": 1, \"results\": []}))\n return\n results = []\n for entry in inputs:\n name = entry.get(\"file\", \"\")\n result = {\"file\": name, \"symbols\": [], \"refs\": []}\n try:\n syms, refs = extract(parse_source(entry.get(\"content\", \"\"), name), module_name_of(name))\n result[\"symbols\"] = syms\n result[\"refs\"] = refs\n except Exception as e:\n result[\"error\"] = str(e) or type(e).__name__\n results.append(result)\n print(json.dumps({\"version\": 1, \"results\": results}))\n\nmain()\n";
|
|
21
|
+
/**
|
|
22
|
+
* Path of `script` written to a PRIVATE, UNPREDICTABLE directory, written once
|
|
23
|
+
* per process.
|
|
24
|
+
*
|
|
25
|
+
* Security: a fixed location such as `os.tmpdir()/ws-py-parse/parse.py` can be
|
|
26
|
+
* pre-created by another local user, who then either symlinks the script path
|
|
27
|
+
* (our write follows the link) or leaves their own `ast.py`/`json.py` next to
|
|
28
|
+
* it — Python puts the script's directory first on `sys.path`, so indexing
|
|
29
|
+
* would run their module. `fs.mkdtemp` creates the directory with a random
|
|
30
|
+
* suffix (0700 on POSIX) and `wx` refuses to write through anything already at
|
|
31
|
+
* the path. The in-flight promise is cached so concurrent first calls share
|
|
32
|
+
* one directory instead of racing to create several.
|
|
33
|
+
*/
|
|
34
|
+
export declare function privateScriptPath(prefix: string, fileName: string, script: string): Promise<string>;
|
|
35
|
+
/** Test-only: every script path written so far (failed writes resolve to ''). */
|
|
36
|
+
export declare function __privateScriptPathsForTest(): Promise<string[]>;
|
|
37
|
+
interface ToolchainSpawnOptions {
|
|
38
|
+
cwd: string;
|
|
39
|
+
env?: NodeJS.ProcessEnv;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run `go run` from the script's own directory, never the indexed project.
|
|
43
|
+
*
|
|
44
|
+
* Launched from the project, the go command reads the project's `go.mod` and
|
|
45
|
+
* `go.work`: a `go`/`toolchain` line newer than the installed Go made every
|
|
46
|
+
* parse fail ("requires go >= …"), dropping the whole repository to the regex
|
|
47
|
+
* fallback — or, with the default `GOTOOLCHAIN=auto`, made indexing download
|
|
48
|
+
* and run whatever toolchain the scanned repository named. The parser only
|
|
49
|
+
* needs the standard library, so it runs outside any module on the local
|
|
50
|
+
* toolchain.
|
|
51
|
+
*/
|
|
52
|
+
export declare function goSpawnOptions(scriptPath: string): ToolchainSpawnOptions;
|
|
53
|
+
/** Python runs from the script directory too, so the project is never on its path. */
|
|
54
|
+
export declare function pySpawnOptions(scriptPath: string): ToolchainSpawnOptions;
|
|
55
|
+
/**
|
|
56
|
+
* Spawn a parser child, feed `stdinPayload`, collect stdout.
|
|
57
|
+
* Resolves `null` on spawn failure or timeout; never rejects.
|
|
58
|
+
*/
|
|
59
|
+
export declare function runToolchainChild(binary: string, args: string[], stdinPayload: string, timeoutMs: number, options: ToolchainSpawnOptions): Promise<{
|
|
60
|
+
code: number | null;
|
|
61
|
+
stdout: string;
|
|
62
|
+
} | null>;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=toolchain-scripts.d.ts.map
|
|
@@ -47,6 +47,20 @@ export interface NodeQueries {
|
|
|
47
47
|
scopeNodes?: ReadonlySet<string>;
|
|
48
48
|
skipNamedChildren?: boolean;
|
|
49
49
|
refRules?: Partial<Record<string, RefRule>>;
|
|
50
|
+
/**
|
|
51
|
+
* Final say on a matched node's kind; `null` skips it. For node types that
|
|
52
|
+
* are only sometimes declarations: C's `declaration` (prototype, global or
|
|
53
|
+
* local), a Ruby `constant` (assignment target or mere reference), an
|
|
54
|
+
* Elixir `call` (`def` or any call at all).
|
|
55
|
+
*/
|
|
56
|
+
resolveKind?: (node: import('web-tree-sitter').Node, kind: SymbolKind) => SymbolKind | null;
|
|
57
|
+
/**
|
|
58
|
+
* Names for nodes that declare several (`int a, b;`) or whose name is not an
|
|
59
|
+
* identifier child. `undefined` falls back to single-name extraction.
|
|
60
|
+
*/
|
|
61
|
+
declaredNames?: (node: import('web-tree-sitter').Node) => readonly string[] | undefined;
|
|
62
|
+
/** Scope membership decided per node; replaces `scopeNodes` when set. */
|
|
63
|
+
isScopeNode?: (node: import('web-tree-sitter').Node) => boolean;
|
|
50
64
|
}
|
|
51
65
|
/** One ref a refRule wants emitted. `callType` defaults to the rule's. */
|
|
52
66
|
export interface RefEmission {
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
* identifiers that we extract from arbitrary child nodes whose byte offset
|
|
9
9
|
* we have to translate into 1-based line + 0-based col ourselves.
|
|
10
10
|
*/
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* 1-based line, 0-based col (the index schema's convention) for an offset,
|
|
13
|
+
* using binary search over newline offsets. The col used to be 1-based —
|
|
14
|
+
* `index - lastNl` counts the newline itself — so every tree-sitter symbol
|
|
15
|
+
* sat one column right of where every other parser puts it.
|
|
16
|
+
*/
|
|
12
17
|
export declare function lineColAt(offsets: readonly number[], index: number): {
|
|
13
18
|
line: number;
|
|
14
19
|
col: number;
|
|
@@ -64,4 +64,11 @@ export declare function parseTreeSitterAst(opts: {
|
|
|
64
64
|
tree: import('web-tree-sitter').Tree;
|
|
65
65
|
parser: import('web-tree-sitter').Parser;
|
|
66
66
|
} | null>;
|
|
67
|
+
/**
|
|
68
|
+
* The identifier a tree-sitter declaration is named by. C/C++ nest it in a
|
|
69
|
+
* declarator chain (`function_definition` → `function_declarator` →
|
|
70
|
+
* `identifier`); comparing or reporting the OUTER declarator's text yields
|
|
71
|
+
* `add(int a, int b)` instead of `add`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function treeSitterDeclarationName(node: import('web-tree-sitter').Node): string | undefined;
|
|
67
74
|
//# sourceMappingURL=tree-sitter-parser.d.ts.map
|
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
statsService,
|
|
10
10
|
symbolGraphService,
|
|
11
11
|
vectorSearchService
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
14
|
-
import "../chunk-
|
|
15
|
-
import "../chunk-
|
|
16
|
-
import "../chunk-
|
|
12
|
+
} from "../chunk-6D3UIQMF.js";
|
|
13
|
+
import "../chunk-BJ5LXLKE.js";
|
|
14
|
+
import "../chunk-R3HR3QI3.js";
|
|
15
|
+
import "../chunk-HSLDY55L.js";
|
|
16
|
+
import "../chunk-OIFY4XNP.js";
|
|
17
17
|
import "../chunk-YOHO4DVT.js";
|
|
18
18
|
import "../chunk-XHSHNOAF.js";
|
|
19
19
|
import "../chunk-XZLVJD56.js";
|
|
@@ -78,19 +78,26 @@ port.on("message", (msg) => {
|
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
void dispatch(msg).then(
|
|
81
|
-
(result) =>
|
|
82
|
-
(err) => {
|
|
81
|
+
(result) => {
|
|
83
82
|
try {
|
|
84
|
-
post({
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
ok: false,
|
|
88
|
-
error: err instanceof Error ? err.message : String(err),
|
|
89
|
-
errorName: err instanceof Error ? err.name : void 0
|
|
90
|
-
});
|
|
91
|
-
} catch {
|
|
83
|
+
post({ type: "response", id: msg.id, ok: true, result });
|
|
84
|
+
} catch (err) {
|
|
85
|
+
postError(msg.id, err);
|
|
92
86
|
}
|
|
93
|
-
}
|
|
87
|
+
},
|
|
88
|
+
(err) => postError(msg.id, err)
|
|
94
89
|
);
|
|
95
90
|
});
|
|
91
|
+
function postError(id, err) {
|
|
92
|
+
try {
|
|
93
|
+
post({
|
|
94
|
+
type: "response",
|
|
95
|
+
id,
|
|
96
|
+
ok: false,
|
|
97
|
+
error: err instanceof Error ? err.message : String(err),
|
|
98
|
+
errorName: err instanceof Error ? err.name : void 0
|
|
99
|
+
});
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
}
|
|
96
103
|
//# sourceMappingURL=worker.js.map
|
|
@@ -34,15 +34,30 @@ export declare function getAllImportRefsWithStatement(stmtFn: (sql: string) => {
|
|
|
34
34
|
export declare function resolveRefsWithStatement(stmtFn: (sql: string) => {
|
|
35
35
|
run: (...args: (string | number)[]) => unknown;
|
|
36
36
|
}): number;
|
|
37
|
+
/**
|
|
38
|
+
* Importer files holding an import ref whose `to_file` is no longer indexed —
|
|
39
|
+
* the target was deleted or renamed. A watcher run only re-resolves the files
|
|
40
|
+
* it touched, and the importer of a deleted file is not one of them, so these
|
|
41
|
+
* are added to its scope; otherwise the edge to the missing file survives.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getFilesWithDanglingImportsWithStatement(stmtFn: (sql: string) => {
|
|
44
|
+
all: () => unknown[];
|
|
45
|
+
}): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Write module-resolution results. `toFile: null` records "re-resolved, no
|
|
48
|
+
* target": it CLEARS a previous `to_file`. Only resolved entries used to be
|
|
49
|
+
* written, so an import whose target had since been deleted kept pointing at
|
|
50
|
+
* it through every later run, full reindex included.
|
|
51
|
+
*/
|
|
37
52
|
export declare function applyImportResolutionsWithStatement(db: {
|
|
38
53
|
exec: (sql: string) => unknown;
|
|
39
54
|
}, stmtFn: (sql: string) => {
|
|
40
|
-
run: (...args: string[]) => unknown;
|
|
55
|
+
run: (...args: (string | null)[]) => unknown;
|
|
41
56
|
}, runWithRetry: <T>(fn: () => T) => T, maxSqlVars: number, resolutions: ReadonlyArray<{
|
|
42
57
|
fromFile: string;
|
|
43
58
|
lang: string;
|
|
44
59
|
module: string;
|
|
45
|
-
toFile: string;
|
|
60
|
+
toFile: string | null;
|
|
46
61
|
}>): number;
|
|
47
62
|
export declare function resolveRefsForNamesUnsafe(stmtFn: (sql: string) => {
|
|
48
63
|
run: (...args: (string | number)[]) => unknown;
|
|
@@ -36,6 +36,15 @@ export interface WriterSearchRow {
|
|
|
36
36
|
*/
|
|
37
37
|
export declare const SEARCH_CANDIDATE_SCAN_CAP = 5000;
|
|
38
38
|
export declare function normalizeSearchLimit(limit: number | undefined): number | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* The symbol kinds a search may return.
|
|
41
|
+
*
|
|
42
|
+
* `undefined` — no kind constraint. `null` — the filter can match nothing (an
|
|
43
|
+
* LSP kind with no internal equivalent, or a `kind` outside the `lspKind`'s
|
|
44
|
+
* set). Otherwise the allowed kinds. `kind` and `lspKind` INTERSECT: the old
|
|
45
|
+
* code let `lspKind` silently override an explicit `kind`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveKindFilter(filter: WriterSearchFilter | undefined): readonly SymbolKind[] | null | undefined;
|
|
39
48
|
export declare function buildWriterSearchWhere(query: string, filter?: WriterSearchFilter | undefined): {
|
|
40
49
|
where: string;
|
|
41
50
|
values: unknown[];
|
|
@@ -18,6 +18,8 @@ export declare class IndexStore {
|
|
|
18
18
|
private readonly stmtCache;
|
|
19
19
|
private bm25Cache;
|
|
20
20
|
private bm25Dirty;
|
|
21
|
+
/** `PRAGMA data_version` the BM25 cache was built at. */
|
|
22
|
+
private bm25DataVersion;
|
|
21
23
|
private stmt;
|
|
22
24
|
constructor(projectRoot: string, opts?: {
|
|
23
25
|
indexDir?: string | undefined;
|
|
@@ -26,6 +28,12 @@ export declare class IndexStore {
|
|
|
26
28
|
runAtomicIndexUpdate<T>(job: () => Promise<T>): Promise<T>;
|
|
27
29
|
private beginWriteTransaction;
|
|
28
30
|
private commitWriteTransaction;
|
|
31
|
+
/**
|
|
32
|
+
* Never throws: every caller is already propagating the failure that caused
|
|
33
|
+
* the rollback. When SQLite has rolled the transaction back itself (disk
|
|
34
|
+
* full, I/O error) the ROLLBACK fails with "no transaction is active", and
|
|
35
|
+
* that message used to replace the real error.
|
|
36
|
+
*/
|
|
29
37
|
private rollbackWriteTransaction;
|
|
30
38
|
private initSchema;
|
|
31
39
|
private static readonly NEXT_SYMBOL_ID_KEY;
|
|
@@ -50,11 +58,12 @@ export declare class IndexStore {
|
|
|
50
58
|
lang: string;
|
|
51
59
|
module: string;
|
|
52
60
|
}>;
|
|
61
|
+
getFilesWithDanglingImports(): string[];
|
|
53
62
|
applyImportResolutions(resolutions: ReadonlyArray<{
|
|
54
63
|
fromFile: string;
|
|
55
64
|
lang: string;
|
|
56
65
|
module: string;
|
|
57
|
-
toFile: string;
|
|
66
|
+
toFile: string | null;
|
|
58
67
|
}>): number;
|
|
59
68
|
search(query: string, filter?: WriterSearchFilter, opts?: {
|
|
60
69
|
limit?: number | undefined;
|
|
@@ -65,7 +74,16 @@ export declare class IndexStore {
|
|
|
65
74
|
total: number;
|
|
66
75
|
};
|
|
67
76
|
private invalidateBm25;
|
|
77
|
+
/**
|
|
78
|
+
* The corpus is rebuilt when this connection wrote (bm25Dirty) OR another
|
|
79
|
+
* connection committed since it was built. Pooled stores stay open for the
|
|
80
|
+
* life of the host while the project daemon, an inline indexer or another
|
|
81
|
+
* process writes the same database; the dirty flag alone never saw those
|
|
82
|
+
* writes, so short-query results silently dropped every symbol added since.
|
|
83
|
+
*/
|
|
68
84
|
private getOrBuildBm25;
|
|
85
|
+
/** Changes whenever ANOTHER connection commits to this database. */
|
|
86
|
+
private dataVersion;
|
|
69
87
|
getAllIndexable(): Array<{
|
|
70
88
|
id: number;
|
|
71
89
|
text: string;
|
|
@@ -174,6 +192,7 @@ export declare class IndexStore {
|
|
|
174
192
|
file: string;
|
|
175
193
|
kind: SymbolKind;
|
|
176
194
|
line: number;
|
|
195
|
+
scope: string;
|
|
177
196
|
}>;
|
|
178
197
|
/** Declarations in one file, in source order. */
|
|
179
198
|
getFileSymbols(file: string, limit: number): Array<{
|
package/dist/edit.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
editTool
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OMWC2OGT.js";
|
|
4
4
|
import "./chunk-MSOAX3GC.js";
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-UMOP3VGK.js";
|
|
6
|
+
import "./chunk-RYLMSGR5.js";
|
|
7
|
+
import "./chunk-6D3UIQMF.js";
|
|
8
|
+
import "./chunk-BJ5LXLKE.js";
|
|
9
|
+
import "./chunk-R3HR3QI3.js";
|
|
10
|
+
import "./chunk-HSLDY55L.js";
|
|
11
|
+
import "./chunk-OIFY4XNP.js";
|
|
12
12
|
import "./chunk-YOHO4DVT.js";
|
|
13
13
|
import "./chunk-XHSHNOAF.js";
|
|
14
14
|
import "./chunk-XZLVJD56.js";
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
GENERIC_MAX_SYMBOLS_DEFAULT,
|
|
4
4
|
parseGeneric,
|
|
5
5
|
parseSymbols
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HSLDY55L.js";
|
|
7
7
|
export {
|
|
8
8
|
GENERIC_MAX_FILE_CHARS,
|
|
9
9
|
GENERIC_MAX_SYMBOLS_DEFAULT,
|
|
10
10
|
parseGeneric,
|
|
11
11
|
parseSymbols
|
|
12
12
|
};
|
|
13
|
-
//# sourceMappingURL=generic-parser-
|
|
13
|
+
//# sourceMappingURL=generic-parser-IVWLFKMR.js.map
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GO_PARSE_SCRIPT,
|
|
3
|
+
goSpawnOptions,
|
|
4
|
+
parseParserOutput,
|
|
5
|
+
privateScriptPath,
|
|
6
|
+
runToolchainChild,
|
|
7
|
+
toIndexSymbols,
|
|
8
|
+
withSpawnGate
|
|
9
|
+
} from "./chunk-OIFY4XNP.js";
|
|
10
|
+
import {
|
|
11
|
+
detectLang
|
|
12
|
+
} from "./chunk-YOHO4DVT.js";
|
|
13
|
+
import {
|
|
14
|
+
resolveWin32Command
|
|
15
|
+
} from "./chunk-XZLVJD56.js";
|
|
16
|
+
|
|
17
|
+
// src/codebase-index/go-parser.ts
|
|
18
|
+
async function parseSymbols(opts) {
|
|
19
|
+
const { file, content, lang } = opts;
|
|
20
|
+
try {
|
|
21
|
+
const parsed = await withSpawnGate(() => syncGoParse(file, content, lang));
|
|
22
|
+
if (parsed.symbols.length > 0) {
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
const fallback = parseGoWithoutToolchain(file, content, lang);
|
|
26
|
+
return parsed.refs?.length ? { ...fallback, refs: parsed.refs } : fallback;
|
|
27
|
+
} catch {
|
|
28
|
+
return parseGoWithoutToolchain(file, content, lang);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
var GO_FUNC_RE = /^func\s+(?:\(\s*(?:[A-Za-z_]\w*\s+)?\*?\s*([A-Za-z_]\w*)(?:\[[^\]]*\])?\s*\)\s*)?([A-Za-z_]\w*)\s*[[(]/;
|
|
32
|
+
var GO_NAME_LIST_RE = /^([A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*)/;
|
|
33
|
+
function maskGoNonCode(src) {
|
|
34
|
+
const blank = (text) => text.replace(/[^\n]/g, " ");
|
|
35
|
+
let out = "";
|
|
36
|
+
let i = 0;
|
|
37
|
+
const n = src.length;
|
|
38
|
+
while (i < n) {
|
|
39
|
+
const c = src[i];
|
|
40
|
+
const next = src[i + 1];
|
|
41
|
+
if (c === "/" && next === "/") {
|
|
42
|
+
const newline = src.indexOf("\n", i);
|
|
43
|
+
const stop = newline === -1 ? n : newline;
|
|
44
|
+
out += blank(src.slice(i, stop));
|
|
45
|
+
i = stop;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (c === "/" && next === "*") {
|
|
49
|
+
const end = src.indexOf("*/", i + 2);
|
|
50
|
+
const stop = end === -1 ? n : end + 2;
|
|
51
|
+
out += blank(src.slice(i, stop));
|
|
52
|
+
i = stop;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
56
|
+
const raw = c === "`";
|
|
57
|
+
let j = i + 1;
|
|
58
|
+
while (j < n && src[j] !== c && (raw || src[j] !== "\n")) {
|
|
59
|
+
j += !raw && src[j] === "\\" ? 2 : 1;
|
|
60
|
+
}
|
|
61
|
+
if (j < n && src[j] === c) {
|
|
62
|
+
out += c + blank(src.slice(i + 1, j)) + c;
|
|
63
|
+
i = j + 1;
|
|
64
|
+
} else {
|
|
65
|
+
const stop = Math.min(j, n);
|
|
66
|
+
out += c + blank(src.slice(i + 1, stop));
|
|
67
|
+
i = stop;
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
out += c;
|
|
72
|
+
i++;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
function parseGoWithoutToolchain(filePath, content, lang = "go") {
|
|
77
|
+
const code = maskGoNonCode(content);
|
|
78
|
+
if (!/^\s*package\s+[A-Za-z_]\w*/m.test(code) || hasUnbalancedDelimiters(code)) {
|
|
79
|
+
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
80
|
+
}
|
|
81
|
+
const symbols = [];
|
|
82
|
+
const packageName = code.match(/^\s*package\s+([A-Za-z_]\w*)/m)?.[1] ?? "";
|
|
83
|
+
const qualify = (...parts) => [packageName, ...parts].filter(Boolean).join(".");
|
|
84
|
+
const codeLines = code.split("\n");
|
|
85
|
+
const srcLines = content.split("\n");
|
|
86
|
+
let braces = 0;
|
|
87
|
+
let parens = 0;
|
|
88
|
+
let group = null;
|
|
89
|
+
for (const [idx, line] of codeLines.entries()) {
|
|
90
|
+
const trimmed = line.trimStart();
|
|
91
|
+
const add = (kind, name, scope) => {
|
|
92
|
+
if (name === "_") return;
|
|
93
|
+
addFallbackSymbol(symbols, {
|
|
94
|
+
filePath,
|
|
95
|
+
lang,
|
|
96
|
+
kind,
|
|
97
|
+
name,
|
|
98
|
+
line: idx + 1,
|
|
99
|
+
col: line.length - trimmed.length,
|
|
100
|
+
signature: (srcLines[idx] ?? "").trim(),
|
|
101
|
+
scope
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
if (braces === 0 && parens === 0) {
|
|
105
|
+
group = null;
|
|
106
|
+
const fn = GO_FUNC_RE.exec(trimmed);
|
|
107
|
+
const opener = /^(const|var|type)\s*\(/.exec(trimmed);
|
|
108
|
+
if (fn?.[2]) {
|
|
109
|
+
add(fn[1] ? "method" : "function", fn[2], fn[1] ? qualify(fn[1], fn[2]) : qualify(fn[2]));
|
|
110
|
+
} else if (opener?.[1]) {
|
|
111
|
+
group = opener[1];
|
|
112
|
+
} else {
|
|
113
|
+
const typeDecl = /^type\s+([A-Za-z_]\w*)/.exec(trimmed);
|
|
114
|
+
if (typeDecl?.[1]) add("type", typeDecl[1], packageName);
|
|
115
|
+
const valueDecl = /^(const|var)\s+/.exec(trimmed);
|
|
116
|
+
const names = valueDecl ? GO_NAME_LIST_RE.exec(trimmed.slice(valueDecl[0].length)) : null;
|
|
117
|
+
if (valueDecl?.[1] && names?.[1]) {
|
|
118
|
+
for (const name of names[1].split(",")) {
|
|
119
|
+
add(valueDecl[1], name.trim(), packageName);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} else if (group && braces === 0 && parens === 1) {
|
|
124
|
+
const names = GO_NAME_LIST_RE.exec(trimmed)?.[1];
|
|
125
|
+
if (names) {
|
|
126
|
+
const members = group === "type" ? [names.split(",")[0] ?? ""] : names.split(",");
|
|
127
|
+
for (const name of members) add(group, name.trim(), packageName);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
for (const ch of line) {
|
|
131
|
+
if (ch === "{") braces++;
|
|
132
|
+
else if (ch === "}") braces--;
|
|
133
|
+
else if (ch === "(") parens++;
|
|
134
|
+
else if (ch === ")") parens--;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return { file: filePath, lang, symbols, mtimeMs: Date.now() };
|
|
138
|
+
}
|
|
139
|
+
function addFallbackSymbol(symbols, opts) {
|
|
140
|
+
symbols.push({
|
|
141
|
+
id: 0,
|
|
142
|
+
lang: opts.lang,
|
|
143
|
+
kind: opts.kind,
|
|
144
|
+
name: opts.name,
|
|
145
|
+
file: opts.filePath,
|
|
146
|
+
line: opts.line,
|
|
147
|
+
col: opts.col,
|
|
148
|
+
signature: opts.signature,
|
|
149
|
+
docComment: "",
|
|
150
|
+
scope: opts.scope,
|
|
151
|
+
text: `${opts.name} ${opts.signature}`.trim()
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function hasUnbalancedDelimiters(content) {
|
|
155
|
+
const pairs = { "(": ")", "[": "]", "{": "}" };
|
|
156
|
+
const closers = new Set(Object.values(pairs));
|
|
157
|
+
const stack = [];
|
|
158
|
+
for (const ch of content) {
|
|
159
|
+
if (pairs[ch]) {
|
|
160
|
+
stack.push(pairs[ch]);
|
|
161
|
+
} else if (closers.has(ch) && stack.pop() !== ch) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return stack.length > 0;
|
|
166
|
+
}
|
|
167
|
+
async function syncGoParse(filePath, content, lang) {
|
|
168
|
+
const empty = { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
169
|
+
try {
|
|
170
|
+
const scriptPath = await privateScriptPath("ws-go-parse-", "parse.go", GO_PARSE_SCRIPT);
|
|
171
|
+
const result = await runToolchainChild(
|
|
172
|
+
resolveWin32Command("go"),
|
|
173
|
+
["run", scriptPath],
|
|
174
|
+
content,
|
|
175
|
+
15e3,
|
|
176
|
+
goSpawnOptions(scriptPath)
|
|
177
|
+
);
|
|
178
|
+
if (result?.code !== 0 || !result.stdout.trim()) return empty;
|
|
179
|
+
const { symbols, refs } = parseParserOutput(result.stdout, lang);
|
|
180
|
+
return {
|
|
181
|
+
file: filePath,
|
|
182
|
+
lang,
|
|
183
|
+
symbols: toIndexSymbols(symbols, filePath, lang),
|
|
184
|
+
refs,
|
|
185
|
+
mtimeMs: Date.now()
|
|
186
|
+
};
|
|
187
|
+
} catch {
|
|
188
|
+
return empty;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
export {
|
|
192
|
+
detectLang,
|
|
193
|
+
maskGoNonCode,
|
|
194
|
+
parseGoWithoutToolchain,
|
|
195
|
+
parseSymbols
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=go-parser-LWV3L5V7.js.map
|