Repository navigation
Expand file tree
/
Copy pathShowParseTree.ts
More file actions
121 lines (106 loc) · 3.19 KB
/
Copy pathShowParseTree.ts
File metadata and controls
121 lines (106 loc) · 3.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { FlashStyle, Range, TextDocument } from "@cursorless/common";
import * as path from "node:path";
import type { Tree, TreeCursor } from "web-tree-sitter";
import type { TreeSitter } from "../typings/TreeSitter";
import { ide } from "../singletons/ide.singleton";
import type { Target } from "../typings/target.types";
import { flashTargets } from "../util/targetUtils";
import type { ActionReturnValue } from "./actions.types";
export default class ShowParseTree {
constructor(private treeSitter: TreeSitter) {
this.run = this.run.bind(this);
}
async run(targets: Target[]): Promise<ActionReturnValue> {
await flashTargets(ide(), targets, FlashStyle.referenced);
const results: string[] = ["# Cursorless parse tree"];
for (const target of targets) {
const { editor, contentRange } = target;
const tree = this.treeSitter.getTree(editor.document);
results.push(parseTree(editor.document, tree, contentRange));
}
ide().openUntitledTextDocument({
language: "markdown",
content: results.join("\n\n"),
});
return { thatTargets: targets };
}
}
function parseTree(
document: TextDocument,
tree: Tree,
contentRange: Range,
): string {
const resultPlayground: string[] = [];
const resultQuery: string[] = [];
parseCursor(resultPlayground, resultQuery, contentRange, tree.walk(), 0);
return [
`## ${path.basename(document.uri.path)} [${contentRange}]\n`,
`\`\`\`${document.languageId}`,
document.getText(contentRange),
"```",
"",
"```scm",
...resultQuery,
"```",
"",
"```js",
...resultPlayground,
"```",
"",
].join("\n");
}
function parseCursor(
resultPlayground: string[],
resultQuery: string[],
contentRange: Range,
cursor: TreeCursor,
numIndents: number,
): void {
while (true) {
const nodeRange = new Range(
cursor.startPosition.row,
cursor.startPosition.column,
cursor.endPosition.row,
cursor.endPosition.column,
);
if (contentRange.intersection(nodeRange) != null) {
const indentation = " ".repeat(numIndents);
const fieldName = getFieldName(cursor);
const prefix = indentation + fieldName;
// Named node
if (cursor.nodeIsNamed) {
resultPlayground.push(`${prefix}${cursor.nodeType} [${nodeRange}]`);
resultQuery.push(`${prefix}(${cursor.nodeType}`);
// Named node with children
if (cursor.gotoFirstChild()) {
parseCursor(
resultPlayground,
resultQuery,
contentRange,
cursor,
numIndents + 1,
);
cursor.gotoParent();
resultQuery.push(`${indentation})`);
}
// Named node without children
else {
resultQuery[resultQuery.length - 1] += ")";
}
}
// Anonymous node
else {
const type = `"${cursor.nodeType}"`;
resultPlayground.push(`${prefix}${type} [${nodeRange}]`);
resultQuery.push(`${prefix}${type}`);
}
}
if (!cursor.gotoNextSibling()) {
return;
}
}
}
function getFieldName(cursor: TreeCursor): string {
const field = cursor.currentFieldName;
return field != null ? `${field}: ` : "";
}