ast-search-python 1.3.0 → 1.4.0
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/AGENTS.md +1 -0
- package/build/ast-print.d.ts +18 -0
- package/build/ast-print.js +51 -0
- package/build/index.d.ts +5 -1
- package/build/index.js +5 -48
- package/build/query.d.ts +1 -1
- package/build/query.js +3 -2
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -18,6 +18,7 @@ ast-search <query> --plugin ast-search-python [--dir <path>] [--format text|json
|
|
|
18
18
|
| `--exclude` | `-x` | none | Glob pattern(s) to exclude from search (repeatable) |
|
|
19
19
|
| `--lang` | `-l` | all | Pass `python` to restrict to Python files only |
|
|
20
20
|
| `--context` | `-C` | `0` | Show N lines of context around each match (like `grep -C`) |
|
|
21
|
+
| `--show-ast` | — | off | Print the tree-sitter AST subtree of each matched node below the match line |
|
|
21
22
|
| `--ast` | — | off | Print Python AST for a snippet or `--file`; requires `--lang python` |
|
|
22
23
|
|
|
23
24
|
**Exit codes:** `0` = matches found · `1` = no matches · `2` = error (invalid selector, etc.)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface TSNodeFull {
|
|
2
|
+
type: string;
|
|
3
|
+
isNamed: boolean;
|
|
4
|
+
text: string;
|
|
5
|
+
children: TSNodeFull[];
|
|
6
|
+
startPosition: {
|
|
7
|
+
row: number;
|
|
8
|
+
column: number;
|
|
9
|
+
};
|
|
10
|
+
endPosition: {
|
|
11
|
+
row: number;
|
|
12
|
+
column: number;
|
|
13
|
+
};
|
|
14
|
+
fieldNameForChild(index: number): string | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function printTSNodeText(node: TSNodeFull, out: string[], indent: string, fieldName?: string): void;
|
|
17
|
+
export declare function printMatchTSNode(node: TSNodeFull): string;
|
|
18
|
+
export declare function serializeTSNode(node: TSNodeFull): Record<string, unknown>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function printTSNodeText(node, out, indent, fieldName) {
|
|
2
|
+
var _a;
|
|
3
|
+
const label = fieldName ? `${fieldName}: ${node.type}` : node.type;
|
|
4
|
+
const namedChildren = node.children.filter((c) => c.isNamed);
|
|
5
|
+
if (namedChildren.length === 0) {
|
|
6
|
+
const text = node.text.replace(/\n/g, "\\n");
|
|
7
|
+
const suffix = text.length <= 60 ? ` [text="${text}"]` : "";
|
|
8
|
+
out.push(`${indent}${label}${suffix}`);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
out.push(`${indent}${label}`);
|
|
12
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
13
|
+
const child = node.children[i];
|
|
14
|
+
if (!child.isNamed)
|
|
15
|
+
continue;
|
|
16
|
+
const childField = (_a = node.fieldNameForChild(i)) !== null && _a !== void 0 ? _a : undefined;
|
|
17
|
+
printTSNodeText(child, out, indent + " ", childField);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function printMatchTSNode(node) {
|
|
22
|
+
const out = [];
|
|
23
|
+
printTSNodeText(node, out, "", undefined);
|
|
24
|
+
return out.join("\n");
|
|
25
|
+
}
|
|
26
|
+
export function serializeTSNode(node) {
|
|
27
|
+
const result = {
|
|
28
|
+
type: node.type,
|
|
29
|
+
isNamed: node.isNamed,
|
|
30
|
+
start: node.startPosition,
|
|
31
|
+
end: node.endPosition,
|
|
32
|
+
};
|
|
33
|
+
const namedChildren = [];
|
|
34
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
35
|
+
const child = node.children[i];
|
|
36
|
+
if (!child.isNamed)
|
|
37
|
+
continue;
|
|
38
|
+
const fieldName = node.fieldNameForChild(i);
|
|
39
|
+
const serialized = serializeTSNode(child);
|
|
40
|
+
if (fieldName)
|
|
41
|
+
serialized.field = fieldName;
|
|
42
|
+
namedChildren.push(serialized);
|
|
43
|
+
}
|
|
44
|
+
if (namedChildren.length > 0) {
|
|
45
|
+
result.children = namedChildren;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
result.text = node.text;
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
package/build/index.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { LanguageBackend, LanguageRegistry, Match } from "ast-search-js/plugin";
|
|
2
|
+
import { printMatchTSNode } from "./ast-print.js";
|
|
3
|
+
export { printMatchTSNode };
|
|
2
4
|
export declare class PythonLanguageBackend implements LanguageBackend {
|
|
3
5
|
readonly langId = "python";
|
|
4
6
|
readonly name = "Python";
|
|
5
7
|
readonly extensions: Set<string>;
|
|
6
8
|
parse(source: string, _filePath: string): Promise<unknown>;
|
|
7
|
-
query(ast: unknown, selector: string, source: string, filePath: string
|
|
9
|
+
query(ast: unknown, selector: string, source: string, filePath: string, options?: {
|
|
10
|
+
showAst?: boolean;
|
|
11
|
+
}): Promise<Match[]>;
|
|
8
12
|
validateSelector(selector: string): Promise<void>;
|
|
9
13
|
printAst(ast: unknown, _source: string, format: "text" | "json"): string;
|
|
10
14
|
}
|
package/build/index.js
CHANGED
|
@@ -11,53 +11,9 @@ import { createRequire } from "module";
|
|
|
11
11
|
import path from "path";
|
|
12
12
|
import { expandShorthands } from "./shorthands.js";
|
|
13
13
|
import { runTreeSitterQuery, validateTreeSitterQuery } from "./query.js";
|
|
14
|
+
import { printTSNodeText, printMatchTSNode, serializeTSNode } from "./ast-print.js";
|
|
15
|
+
export { printMatchTSNode };
|
|
14
16
|
const _require = createRequire(import.meta.url);
|
|
15
|
-
function printTSNodeText(node, out, indent, fieldName) {
|
|
16
|
-
var _a;
|
|
17
|
-
const label = fieldName ? `${fieldName}: ${node.type}` : node.type;
|
|
18
|
-
const namedChildren = node.children.filter((c) => c.isNamed);
|
|
19
|
-
if (namedChildren.length === 0) {
|
|
20
|
-
const text = node.text.replace(/\n/g, "\\n");
|
|
21
|
-
const suffix = text.length <= 60 ? ` [text="${text}"]` : "";
|
|
22
|
-
out.push(`${indent}${label}${suffix}`);
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
out.push(`${indent}${label}`);
|
|
26
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
27
|
-
const child = node.children[i];
|
|
28
|
-
if (!child.isNamed)
|
|
29
|
-
continue;
|
|
30
|
-
const childField = (_a = node.fieldNameForChild(i)) !== null && _a !== void 0 ? _a : undefined;
|
|
31
|
-
printTSNodeText(child, out, indent + " ", childField);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function serializeTSNode(node) {
|
|
36
|
-
const result = {
|
|
37
|
-
type: node.type,
|
|
38
|
-
isNamed: node.isNamed,
|
|
39
|
-
start: node.startPosition,
|
|
40
|
-
end: node.endPosition,
|
|
41
|
-
};
|
|
42
|
-
const namedChildren = [];
|
|
43
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
44
|
-
const child = node.children[i];
|
|
45
|
-
if (!child.isNamed)
|
|
46
|
-
continue;
|
|
47
|
-
const fieldName = node.fieldNameForChild(i);
|
|
48
|
-
const serialized = serializeTSNode(child);
|
|
49
|
-
if (fieldName)
|
|
50
|
-
serialized.field = fieldName;
|
|
51
|
-
namedChildren.push(serialized);
|
|
52
|
-
}
|
|
53
|
-
if (namedChildren.length > 0) {
|
|
54
|
-
result.children = namedChildren;
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
result.text = node.text;
|
|
58
|
-
}
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
17
|
let _runtimePromise = null;
|
|
62
18
|
function getRuntime() {
|
|
63
19
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -90,11 +46,12 @@ export class PythonLanguageBackend {
|
|
|
90
46
|
return parser.parse(source);
|
|
91
47
|
});
|
|
92
48
|
}
|
|
93
|
-
query(ast, selector, source, filePath) {
|
|
49
|
+
query(ast, selector, source, filePath, options) {
|
|
94
50
|
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
var _a;
|
|
95
52
|
const { language } = yield getRuntime();
|
|
96
53
|
const expanded = expandShorthands(selector);
|
|
97
|
-
return runTreeSitterQuery(ast, expanded, source, filePath, language);
|
|
54
|
+
return runTreeSitterQuery(ast, expanded, source, filePath, language, (_a = options === null || options === void 0 ? void 0 : options.showAst) !== null && _a !== void 0 ? _a : false);
|
|
98
55
|
});
|
|
99
56
|
}
|
|
100
57
|
validateSelector(selector) {
|
package/build/query.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { Match } from "ast-search-js/plugin";
|
|
2
|
-
export declare function runTreeSitterQuery(ast: unknown, pattern: string, source: string, filePath: string, language: unknown): Match[];
|
|
2
|
+
export declare function runTreeSitterQuery(ast: unknown, pattern: string, source: string, filePath: string, language: unknown, showAst?: boolean): Match[];
|
|
3
3
|
export declare function validateTreeSitterQuery(pattern: string, language: unknown): void;
|
package/build/query.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { printMatchTSNode } from "./ast-print.js";
|
|
1
2
|
function assertValidPattern(pattern) {
|
|
2
3
|
const trimmed = pattern.trim();
|
|
3
4
|
// A valid tree-sitter pattern must start with "(" (S-expression) or contain
|
|
@@ -7,7 +8,7 @@ function assertValidPattern(pattern) {
|
|
|
7
8
|
`Use a shorthand (e.g. "fn", "call") or write a full S-expression (e.g. "(function_definition) @fn").`);
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
|
-
export function runTreeSitterQuery(ast, pattern, source, filePath, language) {
|
|
11
|
+
export function runTreeSitterQuery(ast, pattern, source, filePath, language, showAst = false) {
|
|
11
12
|
var _a;
|
|
12
13
|
assertValidPattern(pattern);
|
|
13
14
|
const tree = ast;
|
|
@@ -40,7 +41,7 @@ export function runTreeSitterQuery(ast, pattern, source, filePath, language) {
|
|
|
40
41
|
captureMap[cap.name] = source.slice(cap.node.startIndex, cap.node.endIndex);
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
|
-
results.push(Object.assign(Object.assign({ file: filePath, line: anchor.node.startPosition.row + 1, col: anchor.node.startPosition.column, start: anchor.node.startIndex, end: anchor.node.endIndex, source: firstLine }, (text !== firstLine ? { source_full: text } : {})), (Object.keys(captureMap).length > 0 ? { captures: captureMap } : {})));
|
|
44
|
+
results.push(Object.assign(Object.assign(Object.assign({ file: filePath, line: anchor.node.startPosition.row + 1, col: anchor.node.startPosition.column, start: anchor.node.startIndex, end: anchor.node.endIndex, source: firstLine }, (text !== firstLine ? { source_full: text } : {})), (showAst ? { astSubtree: printMatchTSNode(anchor.node) } : {})), (Object.keys(captureMap).length > 0 ? { captures: captureMap } : {})));
|
|
44
45
|
}
|
|
45
46
|
return results;
|
|
46
47
|
}
|