as-test 1.1.5 → 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.
Files changed (43) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +4 -9
  3. package/assembly/index.ts +10 -15
  4. package/assembly/src/expectation.ts +11 -11
  5. package/assembly/src/fuzz.ts +11 -7
  6. package/assembly/src/log.ts +2 -2
  7. package/assembly/src/suite.ts +5 -5
  8. package/assembly/src/tests.ts +8 -8
  9. package/assembly/util/wipc.ts +5 -1
  10. package/bin/build-worker-pool.js +146 -142
  11. package/bin/build-worker.js +37 -34
  12. package/bin/commands/build-core.js +577 -465
  13. package/bin/commands/build.js +49 -29
  14. package/bin/commands/clean-core.js +120 -113
  15. package/bin/commands/clean.js +14 -8
  16. package/bin/commands/doctor-core.js +288 -289
  17. package/bin/commands/doctor.js +1 -1
  18. package/bin/commands/fuzz-core.js +467 -414
  19. package/bin/commands/fuzz.js +27 -10
  20. package/bin/commands/init-core.js +905 -794
  21. package/bin/commands/init.js +2 -2
  22. package/bin/commands/run-core.js +2675 -2344
  23. package/bin/commands/run.js +43 -25
  24. package/bin/commands/test.js +56 -32
  25. package/bin/commands/web-runner-source.js +1 -1
  26. package/bin/commands/web-session.js +516 -525
  27. package/bin/coverage-points.js +363 -341
  28. package/bin/crash-store.js +56 -66
  29. package/bin/index.js +4092 -3150
  30. package/bin/reporters/default.js +1090 -890
  31. package/bin/reporters/tap.js +319 -325
  32. package/bin/types.js +67 -67
  33. package/bin/util.js +1290 -1239
  34. package/bin/wipc.js +70 -73
  35. package/lib/build/index.d.ts +3 -1
  36. package/lib/build/index.js +1039 -1034
  37. package/lib/build/web-runner/client.js +1 -1
  38. package/lib/build/web-runner/html.js +1 -1
  39. package/lib/build/web-runner/worker.js +1 -1
  40. package/package.json +6 -3
  41. package/transform/lib/coverage.js +4 -4
  42. package/transform/lib/log.js +9 -5
  43. package/assembly/util/json.ts +0 -112
@@ -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
- 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(context.visible, context.focus, fallbackType);
17
- if (parameter) {
18
- return {
19
- displayType: parameter.type,
20
- subjectName: parameter.name,
21
- visible: context.visible,
22
- focus: context.focus,
23
- highlightStart: parameter.start,
24
- highlightEnd: parameter.end,
25
- };
26
- }
27
- const ternary = detectCoverageTernary(context.visible, context.focus, fallbackType);
28
- if (ternary) {
29
- return {
30
- displayType: ternary.type,
31
- subjectName: null,
32
- visible: context.visible,
33
- focus: context.focus,
34
- highlightStart: ternary.start,
35
- highlightEnd: ternary.end,
36
- };
37
- }
38
- const ifBranch = detectCoverageIfBranch(context.visible, fallbackType);
39
- if (ifBranch) {
40
- return {
41
- displayType: ifBranch.type,
42
- subjectName: null,
43
- visible: context.visible,
44
- focus: context.focus,
45
- highlightStart: ifBranch.start,
46
- highlightEnd: ifBranch.end,
47
- };
48
- }
49
- const assignment = detectCoverageAssignment(context.visible, fallbackType);
50
- if (assignment) {
51
- return {
52
- displayType: assignment.type,
53
- subjectName: null,
54
- visible: context.visible,
55
- focus: context.focus,
56
- highlightStart: assignment.start,
57
- highlightEnd: assignment.end,
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
- displayType: fallbackType,
99
- subjectName: null,
100
- visible: context.visible,
101
- focus: context.focus,
102
- highlightStart,
103
- highlightEnd,
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
- const resolved = path.resolve(process.cwd(), file);
108
- let lines = sourceLineCache.get(resolved);
109
- if (lines === undefined) {
110
- try {
111
- lines = readFileSync(resolved, "utf8").split(/\r?\n/);
112
- }
113
- catch {
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
- if (!lines)
119
- return "";
120
- return lines[line - 1] ?? "";
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
- if (!visible.length)
124
- return [0, 0];
125
- const index = Math.max(0, Math.min(visible.length - 1, focus));
126
- if (isCoverageBoundary(visible.charAt(index))) {
127
- return [index, Math.min(visible.length, index + 1)];
128
- }
129
- let start = index;
130
- let end = index + 1;
131
- while (start > 0 && !isCoverageBoundary(visible.charAt(start - 1)))
132
- start--;
133
- while (end < visible.length && !isCoverageBoundary(visible.charAt(end)))
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
- const sourceLine = readCoverageSourceLine(file, line);
139
- if (!sourceLine)
140
- return null;
141
- const expanded = sourceLine.replace(/\t/g, " ");
142
- const firstNonWhitespace = expanded.search(/\S/);
143
- if (firstNonWhitespace == -1)
144
- return null;
145
- const visible = expanded.slice(firstNonWhitespace).trimEnd();
146
- if (!visible.length)
147
- return null;
148
- const focus = Math.max(0, Math.min(visible.length - 1, Math.max(0, column - 1 - firstNonWhitespace)));
149
- return { visible, focus };
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
- const trimmed = visible.trim();
153
- if (!trimmed.length)
154
- return null;
155
- let match = trimmed.match(/^(?:export\s+)?function\s+([A-Za-z_]\w*)(?:<[^>]+>)?\s*\(/);
156
- if (match)
157
- return { type: "Function", name: match[1] ?? null };
158
- if (trimmed.startsWith("constructor(") ||
159
- /^(?:public\s+|private\s+|protected\s+)constructor\s*\(/.test(trimmed)) {
160
- return { type: "Constructor", name: "constructor" };
161
- }
162
- match = trimmed.match(/^(?:export\s+)?(?:public\s+|private\s+|protected\s+)?(?:static\s+)?([A-Za-z_]\w*)(?:<[^>]+>)?\([^)]*\)\s*:\s*[^{=]+[{]?$/);
163
- if (match)
164
- return { type: "Method", name: match[1] ?? null };
165
- match = trimmed.match(/^(?:public\s+|private\s+|protected\s+)?(?:readonly\s+)?([A-Za-z_]\w*)(?:<[^>]+>)?\s*:\s*[^=;{]+(?:=.*)?;?$/);
166
- if (match)
167
- return { type: "Property", name: match[1] ?? null };
168
- if (/^(?:export\s+)?class\b/.test(trimmed)) {
169
- match = trimmed.match(/^(?:export\s+)?class\s+([A-Za-z_]\w*)/);
170
- return { type: "Class", name: match?.[1] ?? null };
171
- }
172
- if (/^(?:export\s+)?enum\b/.test(trimmed)) {
173
- match = trimmed.match(/^(?:export\s+)?enum\s+([A-Za-z_]\w*)/);
174
- return { type: "Enum", name: match?.[1] ?? null };
175
- }
176
- if (/^(?:export\s+)?interface\b/.test(trimmed)) {
177
- match = trimmed.match(/^(?:export\s+)?interface\s+([A-Za-z_]\w*)/);
178
- return { type: "Interface", name: match?.[1] ?? null };
179
- }
180
- if (/^(?:export\s+)?namespace\b/.test(trimmed)) {
181
- match = trimmed.match(/^(?:export\s+)?namespace\s+([A-Za-z_]\w*)/);
182
- return { type: "Namespace", name: match?.[1] ?? null };
183
- }
184
- if (/^(?:const|let|var)\b/.test(trimmed)) {
185
- match = trimmed.match(/^(?:const|let|var)\s+([A-Za-z_]\w*)/);
186
- return { type: "Variable", name: match?.[1] ?? null };
187
- }
188
- return null;
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
- const inlineParameter = detectCoverageInlineParameter(visible, focus, fallbackType);
192
- if (inlineParameter) {
193
- return inlineParameter;
194
- }
195
- const openParen = visible.indexOf("(");
196
- const closeParen = visible.lastIndexOf(")");
197
- if (openParen == -1 || closeParen == -1 || closeParen <= openParen) {
198
- return null;
199
- }
200
- if (focus <= openParen || focus >= closeParen) {
201
- return null;
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
- function detectCoverageInlineParameter(visible, focus, fallbackType) {
244
- const match = visible.match(/^([A-Za-z_]\w*)\s*:\s*[^=,]+(?:=\s*[^,]+)?[,]?$/);
245
- if (!match)
246
- return null;
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
- return null;
250
- const nameStart = visible.indexOf(name);
251
- const nameEnd = nameStart + name.length;
252
- const equalsIndex = visible.indexOf("=");
253
- if (fallbackType == "DefaultValue" && equalsIndex != -1) {
254
- const valueStart = skipCoverageWhitespace(visible, equalsIndex + 1);
255
- const [start, end] = resolveCoverageHighlightSpan(visible, Math.max(valueStart, focus));
256
- return {
257
- type: "DefaultValue",
258
- name,
259
- start,
260
- end,
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
- type: fallbackType == "Parameter" ? "Parameter" : "Property",
265
- name,
266
- start: nameStart,
267
- end: nameEnd,
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
- if (fallbackType != "Ternary" && fallbackType != "LogicalBranch") {
272
- return null;
273
- }
274
- const q = visible.indexOf("?");
275
- if (q == -1)
276
- return null;
277
- if (fallbackType == "LogicalBranch") {
278
- const [start, end] = resolveCoverageHighlightSpan(visible, focus);
279
- return { type: "LogicalBranch", start, end };
280
- }
281
- const colon = visible.indexOf(":", q + 1);
282
- if (colon == -1) {
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
- if (fallbackType != "IfBranch")
293
- return null;
294
- const match = visible.match(/^if\s*\(([^)]*)\)/);
295
- if (!match)
296
- return null;
297
- const full = match[0];
298
- const condition = match[1] ?? "";
299
- const openParen = full.indexOf("(");
300
- const conditionPadding = condition.length
301
- ? condition.length - condition.trimStart().length
302
- : 0;
303
- const conditionStart = openParen == -1 ? -1 : openParen + 1 + conditionPadding;
304
- if (conditionStart == -1 || !condition.length) {
305
- return { type: "IfBranch", start: 0, end: full.length };
306
- }
307
- return {
308
- type: "IfBranch",
309
- start: conditionStart,
310
- end: conditionStart + condition.length,
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
- if (fallbackType != "Assignment")
315
- return null;
316
- const match = visible.match(/([A-Za-z_]\w*(?:\.[A-Za-z_]\w*|\[[^\]]+\])?)\s*(=|\+=|-=|\*=|\*\*=|\/=|%=|<<=|>>=|>>>=|&=|\|=|\^=)/);
317
- if (!match)
318
- return null;
319
- const full = match[0];
320
- const lhs = match[1] ?? "";
321
- const operator = match[2] ?? "=";
322
- const fullStart = visible.indexOf(full);
323
- const lhsStart = fullStart + full.indexOf(lhs);
324
- const operatorStart = fullStart + full.lastIndexOf(operator);
325
- return {
326
- type: "Assignment",
327
- start: lhsStart,
328
- end: operatorStart + operator.length,
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
- const matches = [...visible.matchAll(/\b([A-Za-z_]\w*)(?:<[^>()]+>)?\s*\(/g)];
333
- if (!matches.length)
334
- return null;
335
- let bestDistance = Number.POSITIVE_INFINITY;
336
- let bestMatch = null;
337
- for (const match of matches) {
338
- const start = match.index ?? -1;
339
- if (start == -1)
340
- continue;
341
- const end = start + match[0].length;
342
- const distance = focus < start ? start - focus : focus >= end ? focus - end + 1 : 0;
343
- if (distance < bestDistance) {
344
- bestDistance = distance;
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
- const start = bestMatch.index ?? 0;
363
- return {
364
- name,
365
- start,
366
- end: start + (name?.length ?? 1),
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
- return /[\s()[\]{}.,;:+\-*/%&|^!?=<>]/.test(ch);
392
+ return /[\s()[\]{}.,;:+\-*/%&|^!?=<>]/.test(ch);
371
393
  }
372
394
  function skipCoverageWhitespace(visible, index) {
373
- let current = Math.max(0, Math.min(visible.length - 1, index));
374
- while (current < visible.length - 1 && /\s/.test(visible.charAt(current))) {
375
- current++;
376
- }
377
- return current;
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
  }