monocart-coverage-reports 1.1.0 → 2.0.0
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/README.md +116 -29
- package/lib/converter/ast.js +297 -0
- package/lib/converter/branches.js +405 -0
- package/lib/converter/converter.js +720 -237
- package/lib/converter/find-original-range.js +106 -60
- package/lib/converter/info-branch.js +21 -22
- package/lib/converter/info-function.js +6 -18
- package/lib/converter/info-line.js +3 -0
- package/lib/generate.js +4 -0
- package/lib/istanbul/istanbul.js +1 -1
- package/lib/packages/monocart-coverage-v8.js +1 -1
- package/lib/packages/monocart-coverage-vendor.js +21 -14
- package/lib/platform/share.js +104 -3
- package/lib/{converter → utils}/source-path.js +2 -2
- package/lib/utils/util.js +27 -0
- package/lib/v8/v8-summary.js +19 -24
- package/lib/v8/v8.js +66 -28
- package/package.json +24 -29
- package/lib/converter/position-mapping.js +0 -183
- /package/lib/{converter → utils}/dedupe.js +0 -0
package/lib/platform/share.js
CHANGED
|
@@ -194,7 +194,7 @@ const Util = {
|
|
|
194
194
|
return 'high';
|
|
195
195
|
},
|
|
196
196
|
|
|
197
|
-
isJsonType(contentType) {
|
|
197
|
+
isJsonType: (contentType) => {
|
|
198
198
|
if (contentType) {
|
|
199
199
|
if (contentType === 'application/json' || contentType === 'json') {
|
|
200
200
|
return true;
|
|
@@ -203,7 +203,7 @@ const Util = {
|
|
|
203
203
|
return false;
|
|
204
204
|
},
|
|
205
205
|
|
|
206
|
-
isMarkdownType(contentType) {
|
|
206
|
+
isMarkdownType: (contentType) => {
|
|
207
207
|
if (contentType) {
|
|
208
208
|
if (contentType === 'text/markdown' || contentType === 'markdown') {
|
|
209
209
|
return true;
|
|
@@ -212,7 +212,7 @@ const Util = {
|
|
|
212
212
|
return false;
|
|
213
213
|
},
|
|
214
214
|
|
|
215
|
-
isTextType(contentType) {
|
|
215
|
+
isTextType: (contentType) => {
|
|
216
216
|
if (contentType) {
|
|
217
217
|
if (contentType.startsWith('text')) {
|
|
218
218
|
return true;
|
|
@@ -227,6 +227,107 @@ const Util = {
|
|
|
227
227
|
return false;
|
|
228
228
|
},
|
|
229
229
|
|
|
230
|
+
isBlank: (codeStr) => {
|
|
231
|
+
const blankBlock = /\S/;
|
|
232
|
+
return !blankBlock.test(codeStr);
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
getRangeLines: (sLoc, eLoc) => {
|
|
236
|
+
|
|
237
|
+
const lines = [];
|
|
238
|
+
|
|
239
|
+
// invalid location order
|
|
240
|
+
if (sLoc.line > eLoc.line) {
|
|
241
|
+
return lines;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// nothing middle
|
|
245
|
+
if (sLoc.line === eLoc.line && sLoc.column >= eLoc.column) {
|
|
246
|
+
return lines;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const addStart = () => {
|
|
250
|
+
|
|
251
|
+
// nothing start
|
|
252
|
+
if (sLoc.column >= sLoc.length) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const entire = sLoc.column <= sLoc.indent;
|
|
257
|
+
lines.push({
|
|
258
|
+
line: sLoc.line,
|
|
259
|
+
entire,
|
|
260
|
+
range: {
|
|
261
|
+
start: sLoc.column,
|
|
262
|
+
end: sLoc.length
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const addMiddle = () => {
|
|
268
|
+
|
|
269
|
+
const start = sLoc.column <= sLoc.indent;
|
|
270
|
+
const end = Util.isBlank(eLoc.text.slice(eLoc.column));
|
|
271
|
+
const entire = start && end;
|
|
272
|
+
// same line
|
|
273
|
+
lines.push({
|
|
274
|
+
line: sLoc.line,
|
|
275
|
+
entire,
|
|
276
|
+
range: {
|
|
277
|
+
start: sLoc.column,
|
|
278
|
+
end: eLoc.column
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const addEnd = () => {
|
|
284
|
+
|
|
285
|
+
// nothing end
|
|
286
|
+
if (eLoc.column === 0) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const entire = Util.isBlank(eLoc.text.slice(eLoc.column));
|
|
291
|
+
lines.push({
|
|
292
|
+
line: eLoc.line,
|
|
293
|
+
entire,
|
|
294
|
+
range: {
|
|
295
|
+
start: eLoc.indent,
|
|
296
|
+
end: eLoc.column
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// same line, single line
|
|
302
|
+
if (sLoc.line === eLoc.line) {
|
|
303
|
+
|
|
304
|
+
addMiddle();
|
|
305
|
+
|
|
306
|
+
} else {
|
|
307
|
+
// multiple lines
|
|
308
|
+
|
|
309
|
+
addStart();
|
|
310
|
+
|
|
311
|
+
// always entire for middle lines
|
|
312
|
+
const lineStart = sLoc.line + 1;
|
|
313
|
+
const lineEnd = eLoc.line;
|
|
314
|
+
if (lineEnd > lineStart) {
|
|
315
|
+
for (let i = lineStart; i < lineEnd; i++) {
|
|
316
|
+
lines.push({
|
|
317
|
+
line: i,
|
|
318
|
+
entire: true
|
|
319
|
+
// no range for entire line
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// check end
|
|
325
|
+
addEnd();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return lines;
|
|
329
|
+
},
|
|
330
|
+
|
|
230
331
|
// =============================================================================
|
|
231
332
|
// svg
|
|
232
333
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const Util = require('
|
|
2
|
+
const Util = require('./util.js');
|
|
3
3
|
|
|
4
4
|
const resolveUrl = (input) => {
|
|
5
5
|
let url;
|
|
@@ -59,7 +59,7 @@ const getSourcePath = (url, index = '', type = '') => {
|
|
|
59
59
|
|
|
60
60
|
const getSourceType = (sourcePath) => {
|
|
61
61
|
let ext = path.extname(sourcePath);
|
|
62
|
-
let type = '';
|
|
62
|
+
let type = 'js';
|
|
63
63
|
if (ext) {
|
|
64
64
|
ext = ext.slice(1);
|
|
65
65
|
const reg = /^[a-z0-9]+$/;
|
package/lib/utils/util.js
CHANGED
|
@@ -8,6 +8,8 @@ const CG = require('console-grid');
|
|
|
8
8
|
const Share = require('../platform/share.js');
|
|
9
9
|
const { deflateSync } = require('lz-utils');
|
|
10
10
|
|
|
11
|
+
const { acornWalk } = require('../packages/monocart-coverage-vendor.js');
|
|
12
|
+
|
|
11
13
|
const Util = {
|
|
12
14
|
|
|
13
15
|
root: process.cwd(),
|
|
@@ -216,6 +218,31 @@ const Util = {
|
|
|
216
218
|
return '\n';
|
|
217
219
|
},
|
|
218
220
|
|
|
221
|
+
visitAst: (rootNode, visitors) => {
|
|
222
|
+
const baseVisitor = acornWalk.base;
|
|
223
|
+
const parents = [rootNode];
|
|
224
|
+
const visitor = (node, st, override) => {
|
|
225
|
+
const type = override || node.type;
|
|
226
|
+
// console.log('visit', node.type, override, parents.length);
|
|
227
|
+
const handler = visitors[type];
|
|
228
|
+
if (handler) {
|
|
229
|
+
const res = handler(node, parents);
|
|
230
|
+
if (res === 'break') {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const isNew = node !== parents[parents.length - 1];
|
|
235
|
+
if (isNew) {
|
|
236
|
+
parents.push(node);
|
|
237
|
+
}
|
|
238
|
+
baseVisitor[type](node, st, visitor);
|
|
239
|
+
if (isNew) {
|
|
240
|
+
parents.pop();
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
visitor(rootNode);
|
|
244
|
+
},
|
|
245
|
+
|
|
219
246
|
forEachFile: function(dir, extList, callback) {
|
|
220
247
|
if (!fs.existsSync(dir)) {
|
|
221
248
|
return;
|
package/lib/v8/v8-summary.js
CHANGED
|
@@ -1,44 +1,41 @@
|
|
|
1
1
|
// https://playwright.dev/docs/api/class-coverage
|
|
2
2
|
|
|
3
3
|
// url, text, ranges: [ {start, end} ]
|
|
4
|
-
const
|
|
4
|
+
const getCssSummaryBytes = (item) => {
|
|
5
5
|
|
|
6
6
|
const source = item.source;
|
|
7
7
|
const total = source.length;
|
|
8
8
|
|
|
9
9
|
let covered = 0;
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const bytes = item.data.bytes;
|
|
11
|
+
if (bytes) {
|
|
12
|
+
bytes.forEach((range) => {
|
|
13
|
+
if (range.count > 0) {
|
|
14
|
+
covered += range.end - range.start;
|
|
15
|
+
}
|
|
14
16
|
});
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
// no functions for css
|
|
19
20
|
return {
|
|
20
|
-
name: item.sourcePath,
|
|
21
|
-
type: item.type,
|
|
22
|
-
url: item.url,
|
|
23
21
|
total,
|
|
24
|
-
covered
|
|
25
|
-
uncovered
|
|
22
|
+
covered
|
|
26
23
|
};
|
|
27
24
|
|
|
28
25
|
};
|
|
29
26
|
|
|
30
27
|
// url, source, ranges:[{start,end, count}]
|
|
31
|
-
const
|
|
28
|
+
const getJsSummaryBytes = (item) => {
|
|
32
29
|
|
|
33
30
|
const source = item.source;
|
|
34
31
|
const total = source.length;
|
|
35
32
|
|
|
36
|
-
const
|
|
33
|
+
const uncoveredBytes = item.data.bytes.filter((range) => range.count === 0);
|
|
37
34
|
|
|
38
35
|
let uncovered = 0;
|
|
39
36
|
|
|
40
37
|
let endPos = 0;
|
|
41
|
-
|
|
38
|
+
uncoveredBytes.forEach((range) => {
|
|
42
39
|
const { start, end } = range;
|
|
43
40
|
|
|
44
41
|
if (start > endPos) {
|
|
@@ -58,23 +55,21 @@ const getJsSummary = (item) => {
|
|
|
58
55
|
|
|
59
56
|
const covered = total - uncovered;
|
|
60
57
|
|
|
58
|
+
// console.log(item)
|
|
59
|
+
|
|
61
60
|
return {
|
|
62
|
-
name: item.sourcePath,
|
|
63
|
-
type: item.type,
|
|
64
|
-
url: item.url,
|
|
65
61
|
total,
|
|
66
|
-
covered
|
|
67
|
-
uncovered
|
|
62
|
+
covered
|
|
68
63
|
};
|
|
69
64
|
};
|
|
70
65
|
|
|
71
|
-
const
|
|
66
|
+
const getV8SummaryBytes = (item) => {
|
|
72
67
|
if (item.type === 'css') {
|
|
73
|
-
return
|
|
68
|
+
return getCssSummaryBytes(item);
|
|
74
69
|
}
|
|
75
|
-
return
|
|
70
|
+
return getJsSummaryBytes(item);
|
|
76
71
|
};
|
|
77
72
|
|
|
78
73
|
module.exports = {
|
|
79
|
-
|
|
74
|
+
getV8SummaryBytes
|
|
80
75
|
};
|
package/lib/v8/v8.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const Util = require('../utils/util.js');
|
|
3
|
-
const {
|
|
4
|
-
const { dedupeRanges } = require('../
|
|
5
|
-
const { getSourcePath } = require('../
|
|
3
|
+
const { getV8SummaryBytes } = require('./v8-summary.js');
|
|
4
|
+
const { dedupeRanges } = require('../utils/dedupe.js');
|
|
5
|
+
const { getSourcePath } = require('../utils/source-path.js');
|
|
6
6
|
const { mergeScriptCovs } = require('../packages/monocart-coverage-vendor.js');
|
|
7
7
|
const collectSourceMaps = require('../converter/collect-source-maps.js');
|
|
8
|
+
const version = require('../../package.json').version;
|
|
8
9
|
|
|
9
10
|
const initV8ListAndSourcemap = async (v8list, options, inlineSourceMap) => {
|
|
10
11
|
|
|
@@ -73,6 +74,7 @@ const initV8ListAndSourcemap = async (v8list, options, inlineSourceMap) => {
|
|
|
73
74
|
const time_start = Date.now();
|
|
74
75
|
const count = await collectSourceMaps(v8list, options, inlineSourceMap);
|
|
75
76
|
if (count) {
|
|
77
|
+
// debug level time
|
|
76
78
|
Util.logTime(`loaded ${count} sourcemaps`, time_start);
|
|
77
79
|
}
|
|
78
80
|
|
|
@@ -227,48 +229,84 @@ const saveV8HtmlReport = async (reportData, options, v8HtmlOptions) => {
|
|
|
227
229
|
return htmlPath;
|
|
228
230
|
};
|
|
229
231
|
|
|
232
|
+
// calculate uncovered, pct, status
|
|
233
|
+
const calculatePctAndStatus = (item, watermarks) => {
|
|
234
|
+
Object.keys(item).forEach((k) => {
|
|
235
|
+
const indicateData = item[k];
|
|
236
|
+
indicateData.uncovered = indicateData.total - indicateData.covered;
|
|
237
|
+
|
|
238
|
+
let pct = '';
|
|
239
|
+
let status = 'unknown';
|
|
240
|
+
|
|
241
|
+
if (indicateData.total) {
|
|
242
|
+
pct = Util.PNF(indicateData.covered, indicateData.total, 2);
|
|
243
|
+
status = Util.getStatus(pct, watermarks[k]);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
indicateData.pct = pct;
|
|
247
|
+
indicateData.status = status;
|
|
248
|
+
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
|
|
230
252
|
const saveV8Report = async (v8list, options) => {
|
|
231
253
|
const defaultWatermarks = {
|
|
232
|
-
bytes: [50, 80]
|
|
254
|
+
bytes: [50, 80],
|
|
255
|
+
functions: [50, 80],
|
|
256
|
+
branches: [50, 80],
|
|
257
|
+
lines: [50, 80]
|
|
233
258
|
};
|
|
234
259
|
const watermarks = Util.resolveWatermarks(defaultWatermarks, options.watermarks);
|
|
235
260
|
|
|
236
|
-
const bytesWatermarks = watermarks.bytes;
|
|
237
|
-
|
|
238
261
|
// init summary
|
|
239
262
|
v8list.forEach((entry) => {
|
|
240
|
-
entry.summary =
|
|
263
|
+
entry.summary.bytes = getV8SummaryBytes(entry);
|
|
241
264
|
});
|
|
242
265
|
|
|
243
|
-
//
|
|
266
|
+
// overall summary
|
|
244
267
|
const summaryList = v8list.map((entry) => entry.summary);
|
|
245
|
-
|
|
246
|
-
|
|
268
|
+
const summary = {
|
|
269
|
+
bytes: {
|
|
270
|
+
total: 0,
|
|
271
|
+
covered: 0
|
|
272
|
+
},
|
|
273
|
+
functions: {
|
|
274
|
+
total: 0,
|
|
275
|
+
covered: 0
|
|
276
|
+
},
|
|
277
|
+
branches: {
|
|
278
|
+
total: 0,
|
|
279
|
+
covered: 0
|
|
280
|
+
},
|
|
281
|
+
lines: {
|
|
282
|
+
total: 0,
|
|
283
|
+
covered: 0,
|
|
284
|
+
blank: 0,
|
|
285
|
+
comment: 0
|
|
286
|
+
}
|
|
287
|
+
};
|
|
247
288
|
summaryList.forEach((item) => {
|
|
289
|
+
calculatePctAndStatus(item, watermarks);
|
|
290
|
+
Object.keys(item).forEach((k) => {
|
|
291
|
+
const indicateData = item[k];
|
|
292
|
+
if (!indicateData) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
summary[k].total += indicateData.total;
|
|
296
|
+
summary[k].covered += indicateData.covered;
|
|
248
297
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
298
|
+
if (k === 'lines') {
|
|
299
|
+
summary[k].blank += indicateData.blank;
|
|
300
|
+
summary[k].comment += indicateData.comment;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
255
303
|
});
|
|
256
304
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const pct = Util.PNF(covered, total, 2);
|
|
260
|
-
const status = Util.getStatus(pct, bytesWatermarks);
|
|
261
|
-
|
|
262
|
-
const summary = {
|
|
263
|
-
total,
|
|
264
|
-
covered,
|
|
265
|
-
uncovered,
|
|
266
|
-
pct,
|
|
267
|
-
status
|
|
268
|
-
};
|
|
305
|
+
calculatePctAndStatus(summary, watermarks);
|
|
269
306
|
|
|
270
307
|
const reportData = {
|
|
271
308
|
name: options.name,
|
|
309
|
+
version,
|
|
272
310
|
watermarks,
|
|
273
311
|
summary,
|
|
274
312
|
files: v8list
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monocart-coverage-reports",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Monocart coverage reports",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -14,17 +14,20 @@
|
|
|
14
14
|
},
|
|
15
15
|
"types": "./lib/index.d.ts",
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build-test": "npm run build-istanbul && npm run build-v8",
|
|
18
|
-
"build-istanbul": "npx webpack --config mock/webpack.config-istanbul.js",
|
|
19
|
-
"build-v8": "npx webpack --config mock/webpack.config-v8.js",
|
|
20
|
-
"test": "node ./tests/test.js && npm run build-docs",
|
|
21
|
-
"build": "sf lint && sf b -p && npm run build-docs",
|
|
22
|
-
"dev": "sf d v8",
|
|
23
17
|
"build-docs": "node ./scripts/build-docs.js",
|
|
18
|
+
"build-test": "node ./scripts/build-test.js",
|
|
19
|
+
"build": "sf lint && sf b -p && npm run build-test",
|
|
20
|
+
"test-node": "npm run test-node-env && npm run test-node-ins",
|
|
21
|
+
"test-node-env": "cross-env NODE_V8_COVERAGE=.temp/v8-coverage node ./test/test-node-env.js && node ./test/generate-node-report.js",
|
|
22
|
+
"test-node-ins": "node ./test/test-node-ins.js",
|
|
23
|
+
"test-browser": "node ./test/test.js",
|
|
24
|
+
"test": "npm run test-browser && npm run test-node && npm run build-docs",
|
|
25
|
+
"dev": "sf d v8",
|
|
24
26
|
"patch": "npm run build && sf publish patch -r"
|
|
25
27
|
},
|
|
26
28
|
"workspaces": [
|
|
27
|
-
"packages/*"
|
|
29
|
+
"packages/*",
|
|
30
|
+
"test"
|
|
28
31
|
],
|
|
29
32
|
"files": [
|
|
30
33
|
"lib"
|
|
@@ -35,31 +38,23 @@
|
|
|
35
38
|
"url": "git+https://github.com/cenfun/monocart-coverage-reports.git"
|
|
36
39
|
},
|
|
37
40
|
"dependencies": {
|
|
38
|
-
"console-grid": "
|
|
39
|
-
"eight-colors": "
|
|
40
|
-
"istanbul-lib-coverage": "
|
|
41
|
-
"istanbul-lib-report": "
|
|
42
|
-
"istanbul-reports": "
|
|
43
|
-
"lz-utils": "
|
|
44
|
-
"monocart-code-viewer": "^1.0.
|
|
45
|
-
"monocart-formatter": "
|
|
46
|
-
"turbogrid": "
|
|
41
|
+
"console-grid": "~2.0.1",
|
|
42
|
+
"eight-colors": "~1.0.3",
|
|
43
|
+
"istanbul-lib-coverage": "~3.2.2",
|
|
44
|
+
"istanbul-lib-report": "~3.0.1",
|
|
45
|
+
"istanbul-reports": "~3.1.6",
|
|
46
|
+
"lz-utils": "~2.0.1",
|
|
47
|
+
"monocart-code-viewer": "^1.0.12",
|
|
48
|
+
"monocart-formatter": "~2.2.0",
|
|
49
|
+
"turbogrid": "~3.0.12"
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"eslint": "^8.55.0",
|
|
52
|
+
"cross-env": "^7.0.3",
|
|
53
|
+
"eslint": "^8.56.0",
|
|
52
54
|
"eslint-config-plus": "^1.0.6",
|
|
53
55
|
"eslint-plugin-html": "^7.1.0",
|
|
54
56
|
"eslint-plugin-vue": "^9.19.2",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"koa-static-resolver": "^1.0.4",
|
|
58
|
-
"marked": "^11.1.0",
|
|
59
|
-
"playwright": "^1.40.1",
|
|
60
|
-
"stylelint": "^16.0.2",
|
|
61
|
-
"stylelint-config-plus": "^1.0.4",
|
|
62
|
-
"webpack": "^5.89.0",
|
|
63
|
-
"webpack-cli": "^5.1.4"
|
|
57
|
+
"stylelint": "^15.11.0",
|
|
58
|
+
"stylelint-config-plus": "^1.0.4"
|
|
64
59
|
}
|
|
65
60
|
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const findLine = function(list, position) {
|
|
3
|
-
let start = 0;
|
|
4
|
-
let end = list.length - 1;
|
|
5
|
-
while (end - start > 1) {
|
|
6
|
-
const i = Math.floor((start + end) * 0.5);
|
|
7
|
-
const item = list[i];
|
|
8
|
-
if (position < item.start) {
|
|
9
|
-
end = i;
|
|
10
|
-
continue;
|
|
11
|
-
}
|
|
12
|
-
if (position > item.end) {
|
|
13
|
-
start = i;
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
return list[i];
|
|
17
|
-
}
|
|
18
|
-
// last two items, less is start
|
|
19
|
-
const endItem = list[end];
|
|
20
|
-
if (position < endItem.start) {
|
|
21
|
-
return list[start];
|
|
22
|
-
}
|
|
23
|
-
return list[end];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// =======================================================================================
|
|
27
|
-
|
|
28
|
-
const isLineSingleCommented = (codeStr) => {
|
|
29
|
-
const singleBlock = /^\s*\/\//g;
|
|
30
|
-
return singleBlock.test(codeStr);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const isLineStartCommented = (codeStr) => {
|
|
34
|
-
const multiStartBlock = /^\s*\/\*/g;
|
|
35
|
-
return multiStartBlock.test(codeStr);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// multi-comment end but not at end
|
|
39
|
-
const isLineEndCommented = (codeStr) => {
|
|
40
|
-
const multiEndBlock = /.*\*\//g;
|
|
41
|
-
return multiEndBlock.test(codeStr);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const isLineEndCommentedToEnd = (codeStr) => {
|
|
45
|
-
const multiEndBlock = /.*\*\/\s*$/g;
|
|
46
|
-
return multiEndBlock.test(codeStr);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const isLineBlank = (codeStr) => {
|
|
50
|
-
const blankBlock = /\S/;
|
|
51
|
-
return !blankBlock.test(codeStr);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
class PositionMapping {
|
|
55
|
-
constructor(source) {
|
|
56
|
-
this.source = source;
|
|
57
|
-
this.lines = this.buildLines(source);
|
|
58
|
-
this.commentedLines = [];
|
|
59
|
-
this.blankLines = [];
|
|
60
|
-
this.parseLines(this.lines);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// =============================================================================
|
|
64
|
-
|
|
65
|
-
getSlice(s, e) {
|
|
66
|
-
return this.source.slice(s, e);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 1-base
|
|
70
|
-
locationToOffset(location) {
|
|
71
|
-
const { line, column } = location;
|
|
72
|
-
// 1-based
|
|
73
|
-
const lineInfo = this.lines[line - 1];
|
|
74
|
-
if (lineInfo) {
|
|
75
|
-
if (column === Infinity) {
|
|
76
|
-
return lineInfo.start + lineInfo.length;
|
|
77
|
-
}
|
|
78
|
-
return lineInfo.start + column;
|
|
79
|
-
}
|
|
80
|
-
return 0;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 1-base
|
|
84
|
-
offsetToLocation(offset) {
|
|
85
|
-
const lineInfo = findLine(this.lines, offset);
|
|
86
|
-
|
|
87
|
-
let indent = lineInfo.text.search(/\S/);
|
|
88
|
-
if (indent === -1) {
|
|
89
|
-
indent = lineInfo.length;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// 1-base
|
|
93
|
-
const line = lineInfo.line + 1;
|
|
94
|
-
const column = Math.min(Math.max(offset - lineInfo.start, 0), lineInfo.length);
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
... lineInfo,
|
|
98
|
-
line,
|
|
99
|
-
column,
|
|
100
|
-
indent
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// 1-base
|
|
105
|
-
getLine(line) {
|
|
106
|
-
return this.lines[line - 1];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// =============================================================================
|
|
110
|
-
|
|
111
|
-
buildLines(content) {
|
|
112
|
-
let pos = 0;
|
|
113
|
-
const lines = content.split(/\n/).map((text, line) => {
|
|
114
|
-
|
|
115
|
-
let n = 1;
|
|
116
|
-
// may ends with \r (don't consider double \r\r for now)
|
|
117
|
-
const reg = /\r$/;
|
|
118
|
-
if (reg.test(text)) {
|
|
119
|
-
text = text.slice(0, -1);
|
|
120
|
-
n += 1;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const length = text.length;
|
|
124
|
-
const start = pos;
|
|
125
|
-
const end = start + length;
|
|
126
|
-
|
|
127
|
-
pos += length + n;
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
line,
|
|
131
|
-
start,
|
|
132
|
-
end,
|
|
133
|
-
length,
|
|
134
|
-
text
|
|
135
|
-
};
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return lines;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
parseLines(lines) {
|
|
142
|
-
const commentedLines = [];
|
|
143
|
-
const blankLines = [];
|
|
144
|
-
|
|
145
|
-
let startCommented = false;
|
|
146
|
-
|
|
147
|
-
const multiEndHandler = (text, i) => {
|
|
148
|
-
if (isLineEndCommented(text)) {
|
|
149
|
-
startCommented = false;
|
|
150
|
-
if (isLineEndCommentedToEnd(text)) {
|
|
151
|
-
commentedLines.push(i);
|
|
152
|
-
}
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
commentedLines.push(i);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
lines.forEach((line, i) => {
|
|
159
|
-
const text = line.text;
|
|
160
|
-
if (startCommented) {
|
|
161
|
-
return multiEndHandler(text, i);
|
|
162
|
-
}
|
|
163
|
-
if (isLineStartCommented(text)) {
|
|
164
|
-
startCommented = true;
|
|
165
|
-
return multiEndHandler(text, i);
|
|
166
|
-
}
|
|
167
|
-
if (isLineSingleCommented(text)) {
|
|
168
|
-
commentedLines.push(i);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
if (isLineBlank(text)) {
|
|
172
|
-
blankLines.push(i);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
this.commentedLines = commentedLines;
|
|
177
|
-
this.blankLines = blankLines;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
module.exports = PositionMapping;
|
|
183
|
-
|
|
File without changes
|