/
add-braces-to-if-statement.ts
87 lines (70 loc) · 2.17 KB
/
add-braces-to-if-statement.ts
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
import { Editor, Code, ErrorReason } from "../../editor/editor";
import { Selection } from "../../editor/selection";
import { Position } from "../../editor/position";
import * as t from "../../ast";
export { addBracesToIfStatement, hasIfStatementToAddBraces };
async function addBracesToIfStatement(
code: Code,
selection: Selection,
editor: Editor
) {
const updatedCode = updateCode(t.parse(code), selection);
if (!updatedCode.hasCodeChanged) {
editor.showError(ErrorReason.DidNotFindIfStatementToAddBraces);
return;
}
await editor.write(updatedCode.code);
}
function hasIfStatementToAddBraces(ast: t.AST, selection: Selection): boolean {
let result = false;
t.traverseAST(ast, createVisitor(selection, () => (result = true)));
return result;
}
function updateCode(ast: t.AST, selection: Selection): t.Transformed {
return t.transformAST(
ast,
createVisitor(selection, path => {
if (!t.isSelectableNode(path.node.consequent)) return;
const endOfConsequent = Position.fromAST(path.node.consequent.loc.end);
if (selection.start.isBefore(endOfConsequent)) {
path.node.consequent = statementWithBraces(path.node.consequent);
return;
}
if (path.node.alternate) {
path.node.alternate = statementWithBraces(path.node.alternate);
}
})
);
}
function statementWithBraces(node: t.Statement): t.Statement {
return t.isBlockStatement(node) ? node : t.blockStatement([node]);
}
function createVisitor(
selection: Selection,
onMatch: (path: t.NodePath<t.IfStatement>) => void
): t.Visitor {
return {
IfStatement(path) {
if (!selection.isInsidePath(path)) return;
// Since we visit nodes from parent to children, first check
// if a child would match the selection closer.
if (hasChildWhichMatchesSelection(path, selection)) return;
onMatch(path);
path.stop();
}
};
}
function hasChildWhichMatchesSelection(
path: t.NodePath,
selection: Selection
): boolean {
let result = false;
path.traverse({
IfStatement(childPath) {
if (!selection.isInsidePath(childPath)) return;
result = true;
childPath.stop();
}
});
return result;
}