as-test 1.1.6 → 1.1.7
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/CHANGELOG.md +13 -0
- package/README.md +4 -9
- package/assembly/index.ts +10 -15
- package/assembly/src/expectation.ts +11 -11
- package/assembly/src/fuzz.ts +11 -7
- package/assembly/src/log.ts +2 -2
- package/assembly/src/suite.ts +5 -5
- package/assembly/src/tests.ts +8 -8
- package/assembly/util/wipc.ts +5 -1
- package/bin/build-worker-pool.js +146 -142
- package/bin/build-worker.js +37 -34
- package/bin/commands/build-core.js +577 -465
- package/bin/commands/build.js +49 -29
- package/bin/commands/clean-core.js +120 -113
- package/bin/commands/clean.js +14 -8
- package/bin/commands/doctor-core.js +288 -289
- package/bin/commands/doctor.js +1 -1
- package/bin/commands/fuzz-core.js +467 -414
- package/bin/commands/fuzz.js +27 -10
- package/bin/commands/init-core.js +905 -794
- package/bin/commands/init.js +2 -2
- package/bin/commands/run-core.js +2675 -2344
- package/bin/commands/run.js +43 -25
- package/bin/commands/test.js +56 -32
- package/bin/commands/web-runner-source.js +1 -1
- package/bin/commands/web-session.js +516 -525
- package/bin/coverage-points.js +363 -341
- package/bin/crash-store.js +56 -66
- package/bin/index.js +4092 -3150
- package/bin/reporters/default.js +1090 -890
- package/bin/reporters/tap.js +319 -325
- package/bin/types.js +67 -67
- package/bin/util.js +1290 -1239
- package/bin/wipc.js +70 -73
- package/lib/build/index.d.ts +3 -1
- package/lib/build/index.js +1039 -1034
- package/lib/build/web-runner/client.js +1 -1
- package/lib/build/web-runner/html.js +1 -1
- package/lib/build/web-runner/worker.js +1 -1
- package/package.json +6 -3
- package/transform/lib/log.js +9 -5
- package/assembly/util/json.ts +0 -112
package/bin/coverage-points.js
CHANGED
|
@@ -2,377 +2,399 @@ import { readFileSync } from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
const sourceLineCache = new Map();
|
|
4
4
|
export function describeCoveragePoint(file, line, column, fallbackType) {
|
|
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
|
-
const declarationAllowed = fallbackType == "Expression" ||
|
|
61
|
-
fallbackType == "Block" ||
|
|
62
|
-
fallbackType == "Function" ||
|
|
63
|
-
fallbackType == "Method" ||
|
|
64
|
-
fallbackType == "Constructor" ||
|
|
65
|
-
fallbackType == "Variable" ||
|
|
66
|
-
fallbackType == "Property" ||
|
|
67
|
-
fallbackType == "Call";
|
|
68
|
-
const declaration = declarationAllowed
|
|
69
|
-
? detectCoverageDeclaration(context.visible)
|
|
70
|
-
: null;
|
|
71
|
-
if (declaration) {
|
|
72
|
-
const [highlightStart, highlightEnd] = resolveCoverageHighlightSpan(context.visible, context.focus);
|
|
73
|
-
return {
|
|
74
|
-
displayType: declaration.type,
|
|
75
|
-
subjectName: declaration.name,
|
|
76
|
-
visible: context.visible,
|
|
77
|
-
focus: context.focus,
|
|
78
|
-
highlightStart,
|
|
79
|
-
highlightEnd,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
const callAllowed = fallbackType == "Expression" || fallbackType == "Call";
|
|
83
|
-
const call = callAllowed
|
|
84
|
-
? detectCoverageCall(context.visible, context.focus)
|
|
85
|
-
: null;
|
|
86
|
-
if (call) {
|
|
87
|
-
return {
|
|
88
|
-
displayType: "Call",
|
|
89
|
-
subjectName: call.name,
|
|
90
|
-
visible: context.visible,
|
|
91
|
-
focus: context.focus,
|
|
92
|
-
highlightStart: call.start,
|
|
93
|
-
highlightEnd: call.end,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
const [highlightStart, highlightEnd] = resolveCoverageHighlightSpan(context.visible, context.focus);
|
|
5
|
+
const context = getCoverageSourceContext(file, line, column);
|
|
6
|
+
if (!context) {
|
|
7
|
+
return {
|
|
8
|
+
displayType: fallbackType,
|
|
9
|
+
subjectName: null,
|
|
10
|
+
visible: "",
|
|
11
|
+
focus: 0,
|
|
12
|
+
highlightStart: 0,
|
|
13
|
+
highlightEnd: 0,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const parameter = detectCoverageParameter(
|
|
17
|
+
context.visible,
|
|
18
|
+
context.focus,
|
|
19
|
+
fallbackType,
|
|
20
|
+
);
|
|
21
|
+
if (parameter) {
|
|
22
|
+
return {
|
|
23
|
+
displayType: parameter.type,
|
|
24
|
+
subjectName: parameter.name,
|
|
25
|
+
visible: context.visible,
|
|
26
|
+
focus: context.focus,
|
|
27
|
+
highlightStart: parameter.start,
|
|
28
|
+
highlightEnd: parameter.end,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const ternary = detectCoverageTernary(
|
|
32
|
+
context.visible,
|
|
33
|
+
context.focus,
|
|
34
|
+
fallbackType,
|
|
35
|
+
);
|
|
36
|
+
if (ternary) {
|
|
37
|
+
return {
|
|
38
|
+
displayType: ternary.type,
|
|
39
|
+
subjectName: null,
|
|
40
|
+
visible: context.visible,
|
|
41
|
+
focus: context.focus,
|
|
42
|
+
highlightStart: ternary.start,
|
|
43
|
+
highlightEnd: ternary.end,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const ifBranch = detectCoverageIfBranch(context.visible, fallbackType);
|
|
47
|
+
if (ifBranch) {
|
|
48
|
+
return {
|
|
49
|
+
displayType: ifBranch.type,
|
|
50
|
+
subjectName: null,
|
|
51
|
+
visible: context.visible,
|
|
52
|
+
focus: context.focus,
|
|
53
|
+
highlightStart: ifBranch.start,
|
|
54
|
+
highlightEnd: ifBranch.end,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const assignment = detectCoverageAssignment(context.visible, fallbackType);
|
|
58
|
+
if (assignment) {
|
|
97
59
|
return {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
60
|
+
displayType: assignment.type,
|
|
61
|
+
subjectName: null,
|
|
62
|
+
visible: context.visible,
|
|
63
|
+
focus: context.focus,
|
|
64
|
+
highlightStart: assignment.start,
|
|
65
|
+
highlightEnd: assignment.end,
|
|
104
66
|
};
|
|
67
|
+
}
|
|
68
|
+
const declarationAllowed =
|
|
69
|
+
fallbackType == "Expression" ||
|
|
70
|
+
fallbackType == "Block" ||
|
|
71
|
+
fallbackType == "Function" ||
|
|
72
|
+
fallbackType == "Method" ||
|
|
73
|
+
fallbackType == "Constructor" ||
|
|
74
|
+
fallbackType == "Variable" ||
|
|
75
|
+
fallbackType == "Property" ||
|
|
76
|
+
fallbackType == "Call";
|
|
77
|
+
const declaration = declarationAllowed
|
|
78
|
+
? detectCoverageDeclaration(context.visible)
|
|
79
|
+
: null;
|
|
80
|
+
if (declaration) {
|
|
81
|
+
const [highlightStart, highlightEnd] = resolveCoverageHighlightSpan(
|
|
82
|
+
context.visible,
|
|
83
|
+
context.focus,
|
|
84
|
+
);
|
|
85
|
+
return {
|
|
86
|
+
displayType: declaration.type,
|
|
87
|
+
subjectName: declaration.name,
|
|
88
|
+
visible: context.visible,
|
|
89
|
+
focus: context.focus,
|
|
90
|
+
highlightStart,
|
|
91
|
+
highlightEnd,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const callAllowed = fallbackType == "Expression" || fallbackType == "Call";
|
|
95
|
+
const call = callAllowed
|
|
96
|
+
? detectCoverageCall(context.visible, context.focus)
|
|
97
|
+
: null;
|
|
98
|
+
if (call) {
|
|
99
|
+
return {
|
|
100
|
+
displayType: "Call",
|
|
101
|
+
subjectName: call.name,
|
|
102
|
+
visible: context.visible,
|
|
103
|
+
focus: context.focus,
|
|
104
|
+
highlightStart: call.start,
|
|
105
|
+
highlightEnd: call.end,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const [highlightStart, highlightEnd] = resolveCoverageHighlightSpan(
|
|
109
|
+
context.visible,
|
|
110
|
+
context.focus,
|
|
111
|
+
);
|
|
112
|
+
return {
|
|
113
|
+
displayType: fallbackType,
|
|
114
|
+
subjectName: null,
|
|
115
|
+
visible: context.visible,
|
|
116
|
+
focus: context.focus,
|
|
117
|
+
highlightStart,
|
|
118
|
+
highlightEnd,
|
|
119
|
+
};
|
|
105
120
|
}
|
|
106
121
|
export function readCoverageSourceLine(file, line) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
lines = null;
|
|
115
|
-
}
|
|
116
|
-
sourceLineCache.set(resolved, lines);
|
|
122
|
+
const resolved = path.resolve(process.cwd(), file);
|
|
123
|
+
let lines = sourceLineCache.get(resolved);
|
|
124
|
+
if (lines === undefined) {
|
|
125
|
+
try {
|
|
126
|
+
lines = readFileSync(resolved, "utf8").split(/\r?\n/);
|
|
127
|
+
} catch {
|
|
128
|
+
lines = null;
|
|
117
129
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
sourceLineCache.set(resolved, lines);
|
|
131
|
+
}
|
|
132
|
+
if (!lines) return "";
|
|
133
|
+
return lines[line - 1] ?? "";
|
|
121
134
|
}
|
|
122
135
|
export function resolveCoverageHighlightSpan(visible, focus) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
end++;
|
|
135
|
-
return [start, end];
|
|
136
|
+
if (!visible.length) return [0, 0];
|
|
137
|
+
const index = Math.max(0, Math.min(visible.length - 1, focus));
|
|
138
|
+
if (isCoverageBoundary(visible.charAt(index))) {
|
|
139
|
+
return [index, Math.min(visible.length, index + 1)];
|
|
140
|
+
}
|
|
141
|
+
let start = index;
|
|
142
|
+
let end = index + 1;
|
|
143
|
+
while (start > 0 && !isCoverageBoundary(visible.charAt(start - 1))) start--;
|
|
144
|
+
while (end < visible.length && !isCoverageBoundary(visible.charAt(end)))
|
|
145
|
+
end++;
|
|
146
|
+
return [start, end];
|
|
136
147
|
}
|
|
137
148
|
function getCoverageSourceContext(file, line, column) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
const sourceLine = readCoverageSourceLine(file, line);
|
|
150
|
+
if (!sourceLine) return null;
|
|
151
|
+
const expanded = sourceLine.replace(/\t/g, " ");
|
|
152
|
+
const firstNonWhitespace = expanded.search(/\S/);
|
|
153
|
+
if (firstNonWhitespace == -1) return null;
|
|
154
|
+
const visible = expanded.slice(firstNonWhitespace).trimEnd();
|
|
155
|
+
if (!visible.length) return null;
|
|
156
|
+
const focus = Math.max(
|
|
157
|
+
0,
|
|
158
|
+
Math.min(visible.length - 1, Math.max(0, column - 1 - firstNonWhitespace)),
|
|
159
|
+
);
|
|
160
|
+
return { visible, focus };
|
|
150
161
|
}
|
|
151
162
|
function detectCoverageDeclaration(visible) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
163
|
+
const trimmed = visible.trim();
|
|
164
|
+
if (!trimmed.length) return null;
|
|
165
|
+
let match = trimmed.match(
|
|
166
|
+
/^(?:export\s+)?function\s+([A-Za-z_]\w*)(?:<[^>]+>)?\s*\(/,
|
|
167
|
+
);
|
|
168
|
+
if (match) return { type: "Function", name: match[1] ?? null };
|
|
169
|
+
if (
|
|
170
|
+
trimmed.startsWith("constructor(") ||
|
|
171
|
+
/^(?:public\s+|private\s+|protected\s+)constructor\s*\(/.test(trimmed)
|
|
172
|
+
) {
|
|
173
|
+
return { type: "Constructor", name: "constructor" };
|
|
174
|
+
}
|
|
175
|
+
match = trimmed.match(
|
|
176
|
+
/^(?:export\s+)?(?:public\s+|private\s+|protected\s+)?(?:static\s+)?([A-Za-z_]\w*)(?:<[^>]+>)?\([^)]*\)\s*:\s*[^{=]+[{]?$/,
|
|
177
|
+
);
|
|
178
|
+
if (match) return { type: "Method", name: match[1] ?? null };
|
|
179
|
+
match = trimmed.match(
|
|
180
|
+
/^(?:public\s+|private\s+|protected\s+)?(?:readonly\s+)?([A-Za-z_]\w*)(?:<[^>]+>)?\s*:\s*[^=;{]+(?:=.*)?;?$/,
|
|
181
|
+
);
|
|
182
|
+
if (match) return { type: "Property", name: match[1] ?? null };
|
|
183
|
+
if (/^(?:export\s+)?class\b/.test(trimmed)) {
|
|
184
|
+
match = trimmed.match(/^(?:export\s+)?class\s+([A-Za-z_]\w*)/);
|
|
185
|
+
return { type: "Class", name: match?.[1] ?? null };
|
|
186
|
+
}
|
|
187
|
+
if (/^(?:export\s+)?enum\b/.test(trimmed)) {
|
|
188
|
+
match = trimmed.match(/^(?:export\s+)?enum\s+([A-Za-z_]\w*)/);
|
|
189
|
+
return { type: "Enum", name: match?.[1] ?? null };
|
|
190
|
+
}
|
|
191
|
+
if (/^(?:export\s+)?interface\b/.test(trimmed)) {
|
|
192
|
+
match = trimmed.match(/^(?:export\s+)?interface\s+([A-Za-z_]\w*)/);
|
|
193
|
+
return { type: "Interface", name: match?.[1] ?? null };
|
|
194
|
+
}
|
|
195
|
+
if (/^(?:export\s+)?namespace\b/.test(trimmed)) {
|
|
196
|
+
match = trimmed.match(/^(?:export\s+)?namespace\s+([A-Za-z_]\w*)/);
|
|
197
|
+
return { type: "Namespace", name: match?.[1] ?? null };
|
|
198
|
+
}
|
|
199
|
+
if (/^(?:const|let|var)\b/.test(trimmed)) {
|
|
200
|
+
match = trimmed.match(/^(?:const|let|var)\s+([A-Za-z_]\w*)/);
|
|
201
|
+
return { type: "Variable", name: match?.[1] ?? null };
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
189
204
|
}
|
|
190
205
|
function detectCoverageParameter(visible, focus, fallbackType) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
const params = visible.slice(openParen + 1, closeParen);
|
|
204
|
-
const matches = [
|
|
205
|
-
...params.matchAll(/([A-Za-z_]\w*)\s*:\s*[^,)=]+(?:=\s*[^,)]*)?/g),
|
|
206
|
-
];
|
|
207
|
-
if (!matches.length)
|
|
208
|
-
return null;
|
|
209
|
-
for (const match of matches) {
|
|
210
|
-
const localStart = match.index ?? -1;
|
|
211
|
-
if (localStart == -1)
|
|
212
|
-
continue;
|
|
213
|
-
const localEnd = localStart + match[0].length;
|
|
214
|
-
const absoluteStart = openParen + 1 + localStart;
|
|
215
|
-
const absoluteEnd = openParen + 1 + localEnd;
|
|
216
|
-
if (focus < absoluteStart || focus > absoluteEnd)
|
|
217
|
-
continue;
|
|
218
|
-
const name = match[1] ?? null;
|
|
219
|
-
if (!name)
|
|
220
|
-
return null;
|
|
221
|
-
const nameOffset = match[0].indexOf(name);
|
|
222
|
-
const equalsOffset = match[0].indexOf("=");
|
|
223
|
-
if (fallbackType == "DefaultValue" && equalsOffset != -1) {
|
|
224
|
-
const valueStart = absoluteStart + equalsOffset + 1;
|
|
225
|
-
const valueVisibleStart = skipCoverageWhitespace(visible, valueStart);
|
|
226
|
-
const [start, end] = resolveCoverageHighlightSpan(visible, Math.max(valueVisibleStart, focus));
|
|
227
|
-
return {
|
|
228
|
-
type: "DefaultValue",
|
|
229
|
-
name,
|
|
230
|
-
start,
|
|
231
|
-
end,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
return {
|
|
235
|
-
type: fallbackType == "Parameter" ? "Parameter" : "Property",
|
|
236
|
-
name,
|
|
237
|
-
start: absoluteStart + nameOffset,
|
|
238
|
-
end: absoluteStart + nameOffset + name.length,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
206
|
+
const inlineParameter = detectCoverageInlineParameter(
|
|
207
|
+
visible,
|
|
208
|
+
focus,
|
|
209
|
+
fallbackType,
|
|
210
|
+
);
|
|
211
|
+
if (inlineParameter) {
|
|
212
|
+
return inlineParameter;
|
|
213
|
+
}
|
|
214
|
+
const openParen = visible.indexOf("(");
|
|
215
|
+
const closeParen = visible.lastIndexOf(")");
|
|
216
|
+
if (openParen == -1 || closeParen == -1 || closeParen <= openParen) {
|
|
241
217
|
return null;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
218
|
+
}
|
|
219
|
+
if (focus <= openParen || focus >= closeParen) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
const params = visible.slice(openParen + 1, closeParen);
|
|
223
|
+
const matches = [
|
|
224
|
+
...params.matchAll(/([A-Za-z_]\w*)\s*:\s*[^,)=]+(?:=\s*[^,)]*)?/g),
|
|
225
|
+
];
|
|
226
|
+
if (!matches.length) return null;
|
|
227
|
+
for (const match of matches) {
|
|
228
|
+
const localStart = match.index ?? -1;
|
|
229
|
+
if (localStart == -1) continue;
|
|
230
|
+
const localEnd = localStart + match[0].length;
|
|
231
|
+
const absoluteStart = openParen + 1 + localStart;
|
|
232
|
+
const absoluteEnd = openParen + 1 + localEnd;
|
|
233
|
+
if (focus < absoluteStart || focus > absoluteEnd) continue;
|
|
247
234
|
const name = match[1] ?? null;
|
|
248
|
-
if (!name)
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
235
|
+
if (!name) return null;
|
|
236
|
+
const nameOffset = match[0].indexOf(name);
|
|
237
|
+
const equalsOffset = match[0].indexOf("=");
|
|
238
|
+
if (fallbackType == "DefaultValue" && equalsOffset != -1) {
|
|
239
|
+
const valueStart = absoluteStart + equalsOffset + 1;
|
|
240
|
+
const valueVisibleStart = skipCoverageWhitespace(visible, valueStart);
|
|
241
|
+
const [start, end] = resolveCoverageHighlightSpan(
|
|
242
|
+
visible,
|
|
243
|
+
Math.max(valueVisibleStart, focus),
|
|
244
|
+
);
|
|
245
|
+
return {
|
|
246
|
+
type: "DefaultValue",
|
|
247
|
+
name,
|
|
248
|
+
start,
|
|
249
|
+
end,
|
|
250
|
+
};
|
|
262
251
|
}
|
|
263
252
|
return {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
253
|
+
type: fallbackType == "Parameter" ? "Parameter" : "Property",
|
|
254
|
+
name,
|
|
255
|
+
start: absoluteStart + nameOffset,
|
|
256
|
+
end: absoluteStart + nameOffset + name.length,
|
|
268
257
|
};
|
|
258
|
+
}
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
function detectCoverageInlineParameter(visible, focus, fallbackType) {
|
|
262
|
+
const match = visible.match(
|
|
263
|
+
/^([A-Za-z_]\w*)\s*:\s*[^=,]+(?:=\s*[^,]+)?[,]?$/,
|
|
264
|
+
);
|
|
265
|
+
if (!match) return null;
|
|
266
|
+
const name = match[1] ?? null;
|
|
267
|
+
if (!name) return null;
|
|
268
|
+
const nameStart = visible.indexOf(name);
|
|
269
|
+
const nameEnd = nameStart + name.length;
|
|
270
|
+
const equalsIndex = visible.indexOf("=");
|
|
271
|
+
if (fallbackType == "DefaultValue" && equalsIndex != -1) {
|
|
272
|
+
const valueStart = skipCoverageWhitespace(visible, equalsIndex + 1);
|
|
273
|
+
const [start, end] = resolveCoverageHighlightSpan(
|
|
274
|
+
visible,
|
|
275
|
+
Math.max(valueStart, focus),
|
|
276
|
+
);
|
|
277
|
+
return {
|
|
278
|
+
type: "DefaultValue",
|
|
279
|
+
name,
|
|
280
|
+
start,
|
|
281
|
+
end,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
type: fallbackType == "Parameter" ? "Parameter" : "Property",
|
|
286
|
+
name,
|
|
287
|
+
start: nameStart,
|
|
288
|
+
end: nameEnd,
|
|
289
|
+
};
|
|
269
290
|
}
|
|
270
291
|
function detectCoverageTernary(visible, focus, fallbackType) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const [start, end] = resolveCoverageHighlightSpan(visible, focus);
|
|
284
|
-
return { type: "Ternary", start, end };
|
|
285
|
-
}
|
|
286
|
-
const branchStart = focus <= colon ? q + 1 : colon + 1;
|
|
287
|
-
const normalizedStart = skipCoverageWhitespace(visible, branchStart);
|
|
288
|
-
const [start, end] = resolveCoverageHighlightSpan(visible, Math.max(normalizedStart, focus));
|
|
292
|
+
if (fallbackType != "Ternary" && fallbackType != "LogicalBranch") {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
const q = visible.indexOf("?");
|
|
296
|
+
if (q == -1) return null;
|
|
297
|
+
if (fallbackType == "LogicalBranch") {
|
|
298
|
+
const [start, end] = resolveCoverageHighlightSpan(visible, focus);
|
|
299
|
+
return { type: "LogicalBranch", start, end };
|
|
300
|
+
}
|
|
301
|
+
const colon = visible.indexOf(":", q + 1);
|
|
302
|
+
if (colon == -1) {
|
|
303
|
+
const [start, end] = resolveCoverageHighlightSpan(visible, focus);
|
|
289
304
|
return { type: "Ternary", start, end };
|
|
305
|
+
}
|
|
306
|
+
const branchStart = focus <= colon ? q + 1 : colon + 1;
|
|
307
|
+
const normalizedStart = skipCoverageWhitespace(visible, branchStart);
|
|
308
|
+
const [start, end] = resolveCoverageHighlightSpan(
|
|
309
|
+
visible,
|
|
310
|
+
Math.max(normalizedStart, focus),
|
|
311
|
+
);
|
|
312
|
+
return { type: "Ternary", start, end };
|
|
290
313
|
}
|
|
291
314
|
function detectCoverageIfBranch(visible, fallbackType) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
};
|
|
315
|
+
if (fallbackType != "IfBranch") return null;
|
|
316
|
+
const match = visible.match(/^if\s*\(([^)]*)\)/);
|
|
317
|
+
if (!match) return null;
|
|
318
|
+
const full = match[0];
|
|
319
|
+
const condition = match[1] ?? "";
|
|
320
|
+
const openParen = full.indexOf("(");
|
|
321
|
+
const conditionPadding = condition.length
|
|
322
|
+
? condition.length - condition.trimStart().length
|
|
323
|
+
: 0;
|
|
324
|
+
const conditionStart =
|
|
325
|
+
openParen == -1 ? -1 : openParen + 1 + conditionPadding;
|
|
326
|
+
if (conditionStart == -1 || !condition.length) {
|
|
327
|
+
return { type: "IfBranch", start: 0, end: full.length };
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
type: "IfBranch",
|
|
331
|
+
start: conditionStart,
|
|
332
|
+
end: conditionStart + condition.length,
|
|
333
|
+
};
|
|
312
334
|
}
|
|
313
335
|
function detectCoverageAssignment(visible, fallbackType) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
336
|
+
if (fallbackType != "Assignment") return null;
|
|
337
|
+
const match = visible.match(
|
|
338
|
+
/([A-Za-z_]\w*(?:\.[A-Za-z_]\w*|\[[^\]]+\])?)\s*(=|\+=|-=|\*=|\*\*=|\/=|%=|<<=|>>=|>>>=|&=|\|=|\^=)/,
|
|
339
|
+
);
|
|
340
|
+
if (!match) return null;
|
|
341
|
+
const full = match[0];
|
|
342
|
+
const lhs = match[1] ?? "";
|
|
343
|
+
const operator = match[2] ?? "=";
|
|
344
|
+
const fullStart = visible.indexOf(full);
|
|
345
|
+
const lhsStart = fullStart + full.indexOf(lhs);
|
|
346
|
+
const operatorStart = fullStart + full.lastIndexOf(operator);
|
|
347
|
+
return {
|
|
348
|
+
type: "Assignment",
|
|
349
|
+
start: lhsStart,
|
|
350
|
+
end: operatorStart + operator.length,
|
|
351
|
+
};
|
|
330
352
|
}
|
|
331
353
|
function detectCoverageCall(visible, focus) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
bestMatch = match;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (!bestMatch)
|
|
349
|
-
return null;
|
|
350
|
-
const name = bestMatch[1] ?? null;
|
|
351
|
-
if (name == "if" ||
|
|
352
|
-
name == "for" ||
|
|
353
|
-
name == "while" ||
|
|
354
|
-
name == "switch" ||
|
|
355
|
-
name == "return" ||
|
|
356
|
-
name == "function") {
|
|
357
|
-
return null;
|
|
358
|
-
}
|
|
359
|
-
if (bestDistance > Math.max(12, Math.floor(visible.length / 3))) {
|
|
360
|
-
return null;
|
|
354
|
+
const matches = [...visible.matchAll(/\b([A-Za-z_]\w*)(?:<[^>()]+>)?\s*\(/g)];
|
|
355
|
+
if (!matches.length) return null;
|
|
356
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
357
|
+
let bestMatch = null;
|
|
358
|
+
for (const match of matches) {
|
|
359
|
+
const start = match.index ?? -1;
|
|
360
|
+
if (start == -1) continue;
|
|
361
|
+
const end = start + match[0].length;
|
|
362
|
+
const distance =
|
|
363
|
+
focus < start ? start - focus : focus >= end ? focus - end + 1 : 0;
|
|
364
|
+
if (distance < bestDistance) {
|
|
365
|
+
bestDistance = distance;
|
|
366
|
+
bestMatch = match;
|
|
361
367
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
+
}
|
|
369
|
+
if (!bestMatch) return null;
|
|
370
|
+
const name = bestMatch[1] ?? null;
|
|
371
|
+
if (
|
|
372
|
+
name == "if" ||
|
|
373
|
+
name == "for" ||
|
|
374
|
+
name == "while" ||
|
|
375
|
+
name == "switch" ||
|
|
376
|
+
name == "return" ||
|
|
377
|
+
name == "function"
|
|
378
|
+
) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
if (bestDistance > Math.max(12, Math.floor(visible.length / 3))) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
const start = bestMatch.index ?? 0;
|
|
385
|
+
return {
|
|
386
|
+
name,
|
|
387
|
+
start,
|
|
388
|
+
end: start + (name?.length ?? 1),
|
|
389
|
+
};
|
|
368
390
|
}
|
|
369
391
|
function isCoverageBoundary(ch) {
|
|
370
|
-
|
|
392
|
+
return /[\s()[\]{}.,;:+\-*/%&|^!?=<>]/.test(ch);
|
|
371
393
|
}
|
|
372
394
|
function skipCoverageWhitespace(visible, index) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
395
|
+
let current = Math.max(0, Math.min(visible.length - 1, index));
|
|
396
|
+
while (current < visible.length - 1 && /\s/.test(visible.charAt(current))) {
|
|
397
|
+
current++;
|
|
398
|
+
}
|
|
399
|
+
return current;
|
|
378
400
|
}
|