ast-search-python 1.0.2 → 1.0.4
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/README.md +6 -4
- package/build/index.d.ts +4 -4
- package/build/index.js +41 -27
- package/build/query.d.ts +3 -23
- package/build/query.js +9 -6
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ast-search-python
|
|
2
2
|
|
|
3
|
-
Python language plugin for [ast-search](
|
|
3
|
+
Python language plugin for [ast-search-js](../ast-search-js/README.md). Adds `.py` / `.pyw` file support using [tree-sitter](https://tree-sitter.github.io/tree-sitter/) S-expression queries.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
@@ -15,10 +15,12 @@ Python language plugin for [ast-search](../../README.md). Adds `.py` / `.pyw` fi
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install -g ast-search
|
|
18
|
+
npm install -g ast-search-js
|
|
19
19
|
npm install -g ast-search-python
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
> **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.
|
|
23
|
+
|
|
22
24
|
## Usage
|
|
23
25
|
|
|
24
26
|
Pass `--plugin ast-search-python` to enable Python file support:
|
|
@@ -108,10 +110,10 @@ Raw S-expression queries must include at least one `@capture_name` — tree-sitt
|
|
|
108
110
|
|
|
109
111
|
## Plugin API
|
|
110
112
|
|
|
111
|
-
This package implements the `LanguageBackend` interface from `ast-search/plugin`. It registers the `python` language backend automatically when loaded via `--plugin ast-search-python`, or programmatically:
|
|
113
|
+
This package implements the `LanguageBackend` interface from `ast-search-js/plugin`. It registers the `python` language backend automatically when loaded via `--plugin ast-search-python`, or programmatically:
|
|
112
114
|
|
|
113
115
|
```typescript
|
|
114
|
-
import { defaultRegistry } from 'ast-search/plugin';
|
|
116
|
+
import { defaultRegistry } from 'ast-search-js/plugin';
|
|
115
117
|
const { register } = await import('ast-search-python');
|
|
116
118
|
register(defaultRegistry);
|
|
117
119
|
```
|
package/build/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { LanguageBackend, LanguageRegistry, Match } from "ast-search/plugin";
|
|
1
|
+
import type { LanguageBackend, LanguageRegistry, Match } from "ast-search-js/plugin";
|
|
2
2
|
export declare class PythonLanguageBackend implements LanguageBackend {
|
|
3
3
|
readonly langId = "python";
|
|
4
4
|
readonly name = "Python";
|
|
5
5
|
readonly extensions: Set<string>;
|
|
6
|
-
parse(source: string, _filePath: string): unknown
|
|
7
|
-
query(ast: unknown, selector: string, source: string, filePath: string): Match[]
|
|
8
|
-
validateSelector(selector: string): void
|
|
6
|
+
parse(source: string, _filePath: string): Promise<unknown>;
|
|
7
|
+
query(ast: unknown, selector: string, source: string, filePath: string): Promise<Match[]>;
|
|
8
|
+
validateSelector(selector: string): Promise<void>;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Register the Python backend with an ast-search LanguageRegistry.
|
package/build/index.js
CHANGED
|
@@ -1,28 +1,36 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import { createRequire } from "module";
|
|
11
|
+
import path from "path";
|
|
2
12
|
import { expandShorthands } from "./shorthands.js";
|
|
3
13
|
import { runTreeSitterQuery, validateTreeSitterQuery } from "./query.js";
|
|
4
|
-
// tree-sitter is a CommonJS module; use createRequire for ESM compat.
|
|
5
14
|
const _require = createRequire(import.meta.url);
|
|
6
|
-
|
|
7
|
-
// a Python file is actually parsed (not at module import time).
|
|
8
|
-
let _parser;
|
|
9
|
-
let _language;
|
|
10
|
-
let _QueryClass;
|
|
15
|
+
let _runtimePromise = null;
|
|
11
16
|
function getRuntime() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
if (_runtimePromise)
|
|
19
|
+
return _runtimePromise;
|
|
20
|
+
_runtimePromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
const { default: Parser } = yield import("web-tree-sitter");
|
|
22
|
+
const wasmDir = path.dirname(_require.resolve("web-tree-sitter"));
|
|
23
|
+
yield Parser.init({
|
|
24
|
+
locateFile: (_name) => path.join(wasmDir, "tree-sitter.wasm"),
|
|
25
|
+
});
|
|
26
|
+
const wasmPath = path.join(path.dirname(_require.resolve("tree-sitter-wasms/package.json")), "out", "tree-sitter-python.wasm");
|
|
27
|
+
const Python = yield Parser.Language.load(wasmPath);
|
|
28
|
+
const parser = new Parser();
|
|
29
|
+
parser.setLanguage(Python);
|
|
30
|
+
return { parser: parser, language: Python };
|
|
31
|
+
}))();
|
|
32
|
+
return _runtimePromise;
|
|
33
|
+
});
|
|
26
34
|
}
|
|
27
35
|
export class PythonLanguageBackend {
|
|
28
36
|
constructor() {
|
|
@@ -31,17 +39,23 @@ export class PythonLanguageBackend {
|
|
|
31
39
|
this.extensions = new Set([".py", ".pyw"]);
|
|
32
40
|
}
|
|
33
41
|
parse(source, _filePath) {
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const { parser } = yield getRuntime();
|
|
44
|
+
return parser.parse(source);
|
|
45
|
+
});
|
|
36
46
|
}
|
|
37
47
|
query(ast, selector, source, filePath) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
const { language } = yield getRuntime();
|
|
50
|
+
const expanded = expandShorthands(selector);
|
|
51
|
+
return runTreeSitterQuery(ast, expanded, source, filePath, language);
|
|
52
|
+
});
|
|
41
53
|
}
|
|
42
54
|
validateSelector(selector) {
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
56
|
+
const { language } = yield getRuntime();
|
|
57
|
+
validateTreeSitterQuery(expandShorthands(selector), language);
|
|
58
|
+
});
|
|
45
59
|
}
|
|
46
60
|
}
|
|
47
61
|
/**
|
package/build/query.d.ts
CHANGED
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
import type { Match } from "ast-search/plugin";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
row: number;
|
|
5
|
-
column: number;
|
|
6
|
-
};
|
|
7
|
-
startIndex: number;
|
|
8
|
-
endIndex: number;
|
|
9
|
-
type: string;
|
|
10
|
-
}
|
|
11
|
-
interface TSCapture {
|
|
12
|
-
node: TSNode;
|
|
13
|
-
name: string;
|
|
14
|
-
}
|
|
15
|
-
interface TSQueryConstructor {
|
|
16
|
-
new (language: unknown, pattern: string): TSQuery;
|
|
17
|
-
}
|
|
18
|
-
interface TSQuery {
|
|
19
|
-
captures(node: TSNode): TSCapture[];
|
|
20
|
-
}
|
|
21
|
-
export declare function runTreeSitterQuery(ast: unknown, pattern: string, source: string, filePath: string, language: unknown, QueryClass: TSQueryConstructor): Match[];
|
|
22
|
-
export declare function validateTreeSitterQuery(pattern: string, language: unknown, QueryClass: TSQueryConstructor): void;
|
|
23
|
-
export {};
|
|
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[];
|
|
3
|
+
export declare function validateTreeSitterQuery(pattern: string, language: unknown): void;
|
package/build/query.js
CHANGED
|
@@ -7,18 +7,21 @@ function assertValidPattern(pattern) {
|
|
|
7
7
|
`Use a shorthand (e.g. "fn", "call") or write a full S-expression (e.g. "(function_definition) @fn").`);
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
-
export function runTreeSitterQuery(ast, pattern, source, filePath, language
|
|
10
|
+
export function runTreeSitterQuery(ast, pattern, source, filePath, language) {
|
|
11
11
|
assertValidPattern(pattern);
|
|
12
12
|
const tree = ast;
|
|
13
|
-
const q =
|
|
13
|
+
const q = language.query(pattern);
|
|
14
14
|
const captures = q.captures(tree.rootNode);
|
|
15
|
+
// web-tree-sitter may return different JS objects for the same node when
|
|
16
|
+
// multiple capture names match it, so deduplicate by position instead of identity.
|
|
15
17
|
const seen = new Set();
|
|
16
18
|
const results = [];
|
|
17
19
|
for (const capture of captures) {
|
|
18
20
|
const node = capture.node;
|
|
19
|
-
|
|
21
|
+
const key = `${node.startIndex}:${node.endIndex}`;
|
|
22
|
+
if (seen.has(key))
|
|
20
23
|
continue;
|
|
21
|
-
seen.add(
|
|
24
|
+
seen.add(key);
|
|
22
25
|
const text = source.slice(node.startIndex, node.endIndex);
|
|
23
26
|
const firstLine = text.split("\n")[0].trimEnd();
|
|
24
27
|
results.push({
|
|
@@ -30,10 +33,10 @@ export function runTreeSitterQuery(ast, pattern, source, filePath, language, Que
|
|
|
30
33
|
}
|
|
31
34
|
return results;
|
|
32
35
|
}
|
|
33
|
-
export function validateTreeSitterQuery(pattern, language
|
|
36
|
+
export function validateTreeSitterQuery(pattern, language) {
|
|
34
37
|
try {
|
|
35
38
|
assertValidPattern(pattern);
|
|
36
|
-
|
|
39
|
+
language.query(pattern);
|
|
37
40
|
}
|
|
38
41
|
catch (e) {
|
|
39
42
|
throw new Error(`Invalid tree-sitter query: ${e instanceof Error ? e.message : String(e)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ast-search-python",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Python language plugin for ast-search",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/index.js",
|
|
@@ -32,19 +32,19 @@
|
|
|
32
32
|
"provenance": true
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"tree-sitter": "^0.
|
|
36
|
-
"tree-sitter-
|
|
35
|
+
"web-tree-sitter": "^0.24.0",
|
|
36
|
+
"tree-sitter-wasms": "^0.1.12"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@jest/globals": "^29.7.0",
|
|
40
40
|
"@types/jest": "^29.5.12",
|
|
41
41
|
"@types/node": "^20.12.7",
|
|
42
|
-
"ast-search": "
|
|
42
|
+
"ast-search-js": "1.0.1",
|
|
43
43
|
"jest": "^29.7.0",
|
|
44
44
|
"ts-jest": "^29.1.2",
|
|
45
45
|
"typescript": "^5.4.5"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"ast-search": "
|
|
48
|
+
"ast-search-js": "1.0.1"
|
|
49
49
|
}
|
|
50
50
|
}
|