ast-search-python 1.3.0 → 1.4.1

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 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.)
package/README.md CHANGED
@@ -21,8 +21,6 @@ npm install -g ast-search-js
21
21
  npm install -g ast-search-python
22
22
  ```
23
23
 
24
- > **pnpm users:** `tree-sitter` requires compiling a native addon. pnpm blocks build scripts by default, so the binary won't be built without extra steps. Either run `pnpm approve-builds -g` (select `tree-sitter` and `tree-sitter-python`), then reinstall — or use `npm install -g` instead.
25
-
26
24
  ## Usage
27
25
 
28
26
  Pass `--plugin ast-search-python` to enable Python file support:
@@ -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): Promise<Match[]>;
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, offsetEncoding: "bytes", 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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ast-search-python",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "Python language plugin for ast-search",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",