Skip to content

Commit 99eea68

Browse files
brxckpokey
andauthored
Refactor transformations and add name transformation (#56)
* Add name field transformation * Add AST debug logging * Add basic name transformation * Refactor matchers to add function name transform * Add missing function name * Clean up transformations refactor * Refactor branch debug logging * Fix purple color setting name * Add function and class name scopes * Fixed name matching for exported/decorated nodes * Clean up transformations refactor (again) Co-authored-by: Pokey Rule <[email protected]>
1 parent 5271ce3 commit 99eea68

File tree

11 files changed

+434
-295
lines changed

11 files changed

+434
-295
lines changed

src/Types.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,15 @@ export type ScopeType =
4747
| "argumentOrParameter"
4848
| "arrowFunction"
4949
| "class"
50+
| "className"
5051
| "comment"
5152
| "dictionary"
5253
| "functionCall"
54+
| "functionName"
5355
| "ifStatement"
5456
| "list"
5557
| "listElement"
58+
| "name"
5659
| "namedFunction"
5760
| "pair"
5861
| "pairKey"
@@ -244,13 +247,25 @@ export interface Graph {
244247
readonly editStyles: EditStyles;
245248
}
246249

250+
export interface DecorationColorSetting {
251+
dark: string;
252+
light: string;
253+
highContrast: string;
254+
}
255+
247256
export type NodeMatcher = (
248257
editor: vscode.TextEditor,
249258
node: SyntaxNode
250259
) => SelectionWithContext | null;
251260

252-
export interface DecorationColorSetting {
253-
dark: string;
254-
light: string;
255-
highContrast: string;
256-
}
261+
/**
262+
* Returns the desired relative of the provided node.
263+
* Returns null if matching node not found.
264+
**/
265+
export type NodeFinder = (node: SyntaxNode) => SyntaxNode | null;
266+
267+
/** Returns a selection for a given SyntaxNode */
268+
export type SelectionExtractor = (
269+
editor: vscode.TextEditor,
270+
node: SyntaxNode
271+
) => SelectionWithContext | null;

src/debug.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as vscode from "vscode";
2+
import { SyntaxNode } from "web-tree-sitter";
3+
4+
export function logBranchTypes(getNodeAtLocation: any) {
5+
return (event: vscode.TextEditorSelectionChangeEvent) => {
6+
const location = new vscode.Location(
7+
vscode.window.activeTextEditor!.document.uri,
8+
event.selections[0]
9+
);
10+
11+
const ancestors: SyntaxNode[] = [];
12+
let node: SyntaxNode = getNodeAtLocation(location);
13+
while (node.parent != null) {
14+
ancestors.unshift(node.parent);
15+
node = node.parent;
16+
}
17+
18+
ancestors.forEach((node, i) => console.debug(">".repeat(i + 1), node.type));
19+
const leafText = ancestors[ancestors.length - 1].text
20+
.replace(/\s+/g, " ")
21+
.substring(0, 100);
22+
console.debug(">".repeat(ancestors.length), `"${leafText}"`);
23+
};
24+
}

src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
SelectionWithEditor,
1515
} from "./Types";
1616
import makeGraph from "./makeGraph";
17+
import { logBranchTypes } from "./debug";
1718

1819
export async function activate(context: vscode.ExtensionContext) {
1920
const fontMeasurements = new FontMeasurements(context);
@@ -206,6 +207,9 @@ export async function activate(context: vscode.ExtensionContext) {
206207
vscode.window.onDidChangeActiveTextEditor(addDecorationsDebounced),
207208
vscode.window.onDidChangeVisibleTextEditors(addDecorationsDebounced),
208209
vscode.window.onDidChangeTextEditorSelection(addDecorationsDebounced),
210+
vscode.window.onDidChangeTextEditorSelection(
211+
logBranchTypes(getNodeAtLocation)
212+
),
209213
vscode.workspace.onDidChangeTextDocument(handleEdit),
210214
{
211215
dispose() {

src/languages/getPojoMatchers.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,41 @@
11
import { SyntaxNode } from "web-tree-sitter";
2-
import { TextEditor } from "vscode";
3-
import {
4-
delimitedMatcher,
5-
hasType,
6-
simpleSelectionExtractor,
7-
makeRange,
8-
childNodeMatcher,
9-
getNodeWithLeadingDelimiter,
10-
} from "../nodeMatchers";
112
import { getKeyNode, getValueNode } from "../treeSitterUtils";
3+
import {
4+
delimitedSelector,
5+
selectWithLeadingDelimiter,
6+
} from "../nodeSelectors";
7+
import { composedMatcher, matcher, typeMatcher } from "../nodeMatchers";
8+
import { nodeFinder, typedNodeFinder } from "../nodeFinders";
129

10+
// Matchers for "plain old javascript objects", like those found in JSON
1311
export function getPojoMatchers(
1412
dictionaryTypes: string[],
1513
listTypes: string[],
1614
listElementMatcher: (node: SyntaxNode) => boolean
1715
) {
1816
return {
19-
dictionary: hasType(...dictionaryTypes),
20-
pair: delimitedMatcher(
21-
(node) => node.type === "pair",
22-
(node) => node.type === "," || node.type === "}" || node.type === "{",
23-
", "
17+
dictionary: typeMatcher(...dictionaryTypes),
18+
pair: matcher(
19+
nodeFinder((node) => node.type === "pair"),
20+
delimitedSelector(
21+
(node) => node.type === "," || node.type === "}" || node.type === "{",
22+
", "
23+
)
2424
),
25-
pairKey(editor: TextEditor, node: SyntaxNode) {
26-
if (node.type !== "pair") {
27-
return null;
28-
}
29-
30-
return simpleSelectionExtractor(getKeyNode(node)!);
31-
},
32-
value: childNodeMatcher(getValueNode, getNodeWithLeadingDelimiter),
33-
list: hasType(...listTypes),
34-
listElement: delimitedMatcher(
35-
(node) =>
36-
listTypes.includes(node.parent?.type ?? "") && listElementMatcher(node),
37-
(node) => node.type === "," || node.type === "[" || node.type === "]",
38-
", "
25+
pairKey: composedMatcher([typedNodeFinder("pair"), getKeyNode]),
26+
value: matcher(getValueNode, selectWithLeadingDelimiter),
27+
list: typeMatcher(...listTypes),
28+
listElement: matcher(
29+
nodeFinder(
30+
(node) =>
31+
listTypes.includes(node.parent?.type ?? "") &&
32+
listElementMatcher(node)
33+
),
34+
delimitedSelector(
35+
(node) => node.type === "," || node.type === "[" || node.type === "]",
36+
", "
37+
)
3938
),
40-
string: hasType("string"),
39+
string: typeMatcher("string"),
4140
};
4241
}

src/languages/json.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@ const nodeMatchers: Record<ScopeType, NodeMatcher> = {
2525
...getPojoMatchers(["object"], ["array"], isValue),
2626
ifStatement: notSupported,
2727
class: notSupported,
28+
className: notSupported,
2829
statement: notSupported,
2930
arrowFunction: notSupported,
3031
functionCall: notSupported,
3132
argumentOrParameter: notSupported,
3233
namedFunction: notSupported,
34+
functionName: notSupported,
3335
comment: notSupported,
3436
type: notSupported,
37+
name: notSupported,
3538
};
3639

3740
export default nodeMatchers;

src/languages/python.ts

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
import { SyntaxNode } from "web-tree-sitter";
22
import { getPojoMatchers } from "./getPojoMatchers";
33
import {
4-
childNodeMatcher,
5-
delimitedMatcher,
6-
getNodeWithLeadingDelimiter,
7-
hasType,
8-
possiblyWrappedNode,
4+
cascadingMatcher,
5+
composedMatcher,
6+
matcher,
7+
notSupported,
8+
typeMatcher,
99
} from "../nodeMatchers";
10-
import { NodeMatcher, ScopeType } from "../Types";
11-
import { getDefinitionNode } from "../treeSitterUtils";
10+
import { NodeFinder, NodeMatcher, ScopeType } from "../Types";
11+
import {
12+
getDefinitionNode,
13+
getLeftNode,
14+
getNameNode,
15+
} from "../treeSitterUtils";
16+
import {
17+
nodeFinder,
18+
typedNodeFinder,
19+
findPossiblyWrappedNode,
20+
} from "../nodeFinders";
21+
import {
22+
delimitedSelector,
23+
selectWithLeadingDelimiter,
24+
} from "../nodeSelectors";
1225

1326
// TODO figure out how to properly use super types
1427
// Generated by the following command:
@@ -113,10 +126,10 @@ const ARGUMENT_TYPES = [
113126
"keyword_argument",
114127
];
115128

116-
function possiblyDecoratedDefinition(...typeNames: string[]): NodeMatcher {
117-
return possiblyWrappedNode(
118-
(node) => node.type === "decorated_definition",
119-
(node) => typeNames.includes(node.type),
129+
function possiblyDecoratedDefinition(...typeNames: string[]): NodeFinder {
130+
return findPossiblyWrappedNode(
131+
typedNodeFinder("decorated_definition"),
132+
typedNodeFinder(...typeNames),
120133
(node) => [getDefinitionNode(node)]
121134
);
122135
}
@@ -130,23 +143,39 @@ const nodeMatchers: Record<ScopeType, NodeMatcher> = {
130143
["list", "list_comprehension"],
131144
(node) => LIST_ELEMENT_TYPES.includes(node.type)
132145
),
133-
ifStatement: hasType("if_statement"),
134-
class: possiblyDecoratedDefinition("class_definition"),
135-
statement: hasType(...STATEMENT_TYPES),
136-
arrowFunction: hasType("lambda"),
137-
functionCall: hasType("call"),
138-
argumentOrParameter: delimitedMatcher(
139-
(node) =>
140-
(node.parent?.type === "argument_list" &&
141-
ARGUMENT_TYPES.includes(node.type)) ||
142-
(PARAMETER_LIST_TYPES.includes(node.parent?.type ?? "") &&
143-
PARAMETER_TYPES.includes(node.type)),
144-
(node) => node.type === "," || node.type === "(" || node.type === ")",
145-
", "
146+
ifStatement: typeMatcher("if_statement"),
147+
class: matcher(possiblyDecoratedDefinition("class_definition")),
148+
statement: typeMatcher(...STATEMENT_TYPES),
149+
name: cascadingMatcher(
150+
matcher(getNameNode),
151+
matcher((node) => (node.type === "assignment" ? getLeftNode(node) : null))
152+
),
153+
functionName: composedMatcher([
154+
typedNodeFinder("function_definition"),
155+
getNameNode,
156+
]),
157+
className: composedMatcher([
158+
typedNodeFinder("class_definition"),
159+
getNameNode,
160+
]),
161+
arrowFunction: typeMatcher("lambda"),
162+
functionCall: typeMatcher("call"),
163+
argumentOrParameter: matcher(
164+
nodeFinder(
165+
(node) =>
166+
(node.parent?.type === "argument_list" &&
167+
ARGUMENT_TYPES.includes(node.type)) ||
168+
(PARAMETER_LIST_TYPES.includes(node.parent?.type ?? "") &&
169+
PARAMETER_TYPES.includes(node.type))
170+
),
171+
delimitedSelector(
172+
(node) => node.type === "," || node.type === "(" || node.type === ")",
173+
", "
174+
)
146175
),
147-
namedFunction: possiblyDecoratedDefinition("function_definition"),
148-
comment: hasType("comment"),
149-
type: childNodeMatcher(getTypeNode, getNodeWithLeadingDelimiter),
176+
namedFunction: matcher(possiblyDecoratedDefinition("function_definition")),
177+
comment: typeMatcher("comment"),
178+
type: matcher(getTypeNode, selectWithLeadingDelimiter),
150179
};
151180

152181
export default nodeMatchers;

0 commit comments

Comments
 (0)