This repository was archived by the owner on Dec 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
/
Copy pathprepend_statements.hack
98 lines (89 loc) · 2.77 KB
/
prepend_statements.hack
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
/*
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
namespace Facebook\HHAST;
use namespace HH\Lib\{C, Vec};
use function Facebook\HHAST\__Private\whitespace_from_nodelist;
/**
* Adds the provided statement to the closest reasonable position _before_ the
* specified node. For example, if the `$before` node is part of a larger
* statement, the provided `$statement` will not be added directly before the
* node, but it will be added before the whole larger statement.
*
* Tries to make the formatting consistent with the rest of the block into which
* the statement is inserted.
*/
function prepend_statements(
Script $root,
vec<IStatement> $new,
Node $before,
): Script {
if (C\is_empty($new)) {
return $root;
}
// Find the closest parent block.
$ancestors = $root->getAncestorsOfDescendant($before);
for ($i = C\count($ancestors) - 1; $i >= 0; --$i) {
if ($ancestors[$i] is IStatement) {
break;
}
}
for (--$i; $i >= 0; --$i) {
if ($ancestors[$i] is NodeList<_>) {
break;
}
}
invariant(
$i >= 0,
'Failed to find any parent NodeList of any parent Statement.',
);
$parent_block = $ancestors[$i] as NodeList<_>;
$old = $parent_block->getChildren();
for ($before_idx = 0; $before_idx < C\count($old); ++$before_idx) {
if ($old[$before_idx] === $ancestors[$i + 1]) {
break;
}
}
invariant(
$before_idx < C\count($old),
'Failed to find the provided statement in the parent block. This should '.
'never happen.',
);
// Move leading trivia (both whitespace and comments) from the current
// statement to the first statement being prepended -- i.e. we want to insert
// the new statements _inbetween_ the current statement and its leading
// trivia.
$new[0] = $new[0]->replace(
$new[0]->getFirstTokenx(),
$new[0]->getFirstTokenx()
->withLeading($old[$before_idx]->getFirstTokenx()->getLeading()),
);
$old[$before_idx] = $old[$before_idx]->replace(
$old[$before_idx]->getFirstTokenx(),
$old[$before_idx]->getFirstTokenx()->withLeading(null),
);
// Put an autodetected (via whitespace_from_nodelist) amount of whitespace
// between each pair of statements.
$whitespace_between = whitespace_from_nodelist($parent_block);
foreach ($new as $idx => $statement) {
$new[$idx] = $statement->replace(
$statement->getLastTokenx(),
$statement->getLastTokenx()->withTrailing($whitespace_between),
);
}
return $root->replace(
$parent_block,
NodeList::createMaybeEmptyList(
Vec\concat(
Vec\take($old, $before_idx),
$new,
Vec\drop($old, $before_idx),
),
),
);
}