markdownlint 0.20.2 → 0.21.1
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/CONTRIBUTING.md +50 -47
- package/LICENSE +21 -21
- package/README.md +915 -871
- package/demo/markdownlint-browser.js +4693 -5400
- package/doc/CustomRules.md +315 -313
- package/doc/Rules.md +1868 -1795
- package/helpers/LICENSE +21 -21
- package/helpers/README.md +50 -50
- package/helpers/helpers.js +690 -671
- package/helpers/package.json +18 -18
- package/lib/cache.js +24 -24
- package/lib/markdownlint.d.ts +365 -352
- package/lib/markdownlint.js +1186 -1101
- package/lib/md001.js +22 -22
- package/lib/md002.js +22 -22
- package/lib/md003.js +42 -42
- package/lib/md004.js +43 -43
- package/lib/md005.js +69 -69
- package/lib/md006.js +34 -34
- package/lib/md007.js +47 -47
- package/lib/md009.js +77 -77
- package/lib/md010.js +38 -38
- package/lib/md011.js +42 -38
- package/lib/md012.js +32 -32
- package/lib/md013.js +99 -99
- package/lib/md014.js +53 -53
- package/lib/md018.js +34 -34
- package/lib/md019.js +39 -39
- package/lib/md020.js +62 -62
- package/lib/md021.js +60 -60
- package/lib/md022.js +56 -56
- package/lib/md023.js +38 -38
- package/lib/md024.js +38 -38
- package/lib/md025.js +32 -32
- package/lib/md026.js +40 -40
- package/lib/md027.js +56 -56
- package/lib/md028.js +38 -38
- package/lib/md029.js +66 -66
- package/lib/md030.js +50 -50
- package/lib/md031.js +40 -39
- package/lib/md032.js +50 -50
- package/lib/md033.js +45 -45
- package/lib/md034.js +61 -61
- package/lib/md035.js +21 -21
- package/lib/md036.js +56 -56
- package/lib/md037.js +150 -141
- package/lib/md038.js +68 -68
- package/lib/md039.js +64 -64
- package/lib/md040.js +18 -18
- package/lib/md041.js +31 -31
- package/lib/md042.js +42 -41
- package/lib/md043.js +45 -45
- package/lib/md044.js +94 -71
- package/lib/md045.js +18 -18
- package/lib/md046.js +32 -32
- package/lib/md047.js +28 -28
- package/lib/md048.js +29 -29
- package/lib/rules.js +60 -61
- package/package.json +74 -67
- package/schema/build-config-schema.js +409 -405
- package/schema/markdownlint-config-schema.json +7 -2
- package/style/all.json +5 -5
- package/style/cirosantilli.json +22 -22
- package/style/relaxed.json +13 -13
package/helpers/helpers.js
CHANGED
|
@@ -1,671 +1,690 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
"use strict";
|
|
4
|
-
|
|
5
|
-
const os = require("os");
|
|
6
|
-
|
|
7
|
-
// Regular expression for matching common newline characters
|
|
8
|
-
// See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
|
|
9
|
-
const newLineRe = /\r\n?|\n/g;
|
|
10
|
-
module.exports.newLineRe = newLineRe;
|
|
11
|
-
|
|
12
|
-
// Regular expression for matching common front matter (YAML and TOML)
|
|
13
|
-
module.exports.frontMatterRe =
|
|
14
|
-
// eslint-disable-next-line max-len
|
|
15
|
-
/((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$))(\r\n|\r|\n|$)/m;
|
|
16
|
-
|
|
17
|
-
// Regular expression for matching inline disable/enable comments
|
|
18
|
-
const inlineCommentRe =
|
|
19
|
-
// eslint-disable-next-line max-len
|
|
20
|
-
/<!--\s*markdownlint-(?:(?:(disable|enable|capture|restore|disable-file|enable-file)((?:\s+[a-z0-9_-]+)*))|(?:(configure-file)\s+([\s\S]*?)))\s*-->/ig;
|
|
21
|
-
module.exports.inlineCommentRe = inlineCommentRe;
|
|
22
|
-
|
|
23
|
-
// Regular expressions for range matching
|
|
24
|
-
module.exports.bareUrlRe = /(?:http|ftp)s?:\/\/[^\s\]"']
|
|
25
|
-
module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
|
|
26
|
-
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
|
|
27
|
-
|
|
28
|
-
// Regular expression for emphasis markers
|
|
29
|
-
const emphasisMarkersRe = /[_*]
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
module.exports.
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
module.exports.
|
|
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
|
-
module.exports.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return "
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
*
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
231
|
-
lineMetadata[i][2] = true;
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
filterTokens(params, "
|
|
235
|
-
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
236
|
-
lineMetadata[i][
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
filterTokens(params, "
|
|
240
|
-
let
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
lineMetadata
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
"
|
|
280
|
-
"
|
|
281
|
-
|
|
282
|
-
"
|
|
283
|
-
"
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
current.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
} else if (token.
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
let
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
//
|
|
404
|
-
|
|
405
|
-
currentColumn
|
|
406
|
-
} else
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
* @
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
"
|
|
580
|
-
|
|
581
|
-
"
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
* @
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
return
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
(
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
lastFixInfo.lineNumber
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
(
|
|
661
|
-
|
|
662
|
-
(deleteCount
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
// Regular expression for matching common newline characters
|
|
8
|
+
// See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
|
|
9
|
+
const newLineRe = /\r\n?|\n/g;
|
|
10
|
+
module.exports.newLineRe = newLineRe;
|
|
11
|
+
|
|
12
|
+
// Regular expression for matching common front matter (YAML and TOML)
|
|
13
|
+
module.exports.frontMatterRe =
|
|
14
|
+
// eslint-disable-next-line max-len
|
|
15
|
+
/((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$))(\r\n|\r|\n|$)/m;
|
|
16
|
+
|
|
17
|
+
// Regular expression for matching inline disable/enable comments
|
|
18
|
+
const inlineCommentRe =
|
|
19
|
+
// eslint-disable-next-line max-len
|
|
20
|
+
/<!--\s*markdownlint-(?:(?:(disable|enable|capture|restore|disable-file|enable-file)((?:\s+[a-z0-9_-]+)*))|(?:(configure-file)\s+([\s\S]*?)))\s*-->/ig;
|
|
21
|
+
module.exports.inlineCommentRe = inlineCommentRe;
|
|
22
|
+
|
|
23
|
+
// Regular expressions for range matching
|
|
24
|
+
module.exports.bareUrlRe = /(?:http|ftp)s?:\/\/[^\s\]"']*(?:\/|[^\s\]"'\W])/ig;
|
|
25
|
+
module.exports.listItemMarkerRe = /^([\s>]*)(?:[*+-]|\d+[.)])\s+/;
|
|
26
|
+
module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
|
|
27
|
+
|
|
28
|
+
// Regular expression for all instances of emphasis markers
|
|
29
|
+
const emphasisMarkersRe = /[_*]/g;
|
|
30
|
+
|
|
31
|
+
// Regular expression for inline links and shortcut reference links
|
|
32
|
+
const linkRe = /\[(?:[^[\]]|\[[^\]]*\])*\](?:\(\S*\))?/g;
|
|
33
|
+
|
|
34
|
+
// readFile options for reading with the UTF-8 encoding
|
|
35
|
+
module.exports.utf8Encoding = "utf8";
|
|
36
|
+
|
|
37
|
+
// All punctuation characters (normal and full-width)
|
|
38
|
+
module.exports.allPunctuation = ".,;:!?。,;:!?";
|
|
39
|
+
|
|
40
|
+
// Returns true iff the input is a number
|
|
41
|
+
module.exports.isNumber = function isNumber(obj) {
|
|
42
|
+
return typeof obj === "number";
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Returns true iff the input is a string
|
|
46
|
+
module.exports.isString = function isString(obj) {
|
|
47
|
+
return typeof obj === "string";
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Returns true iff the input string is empty
|
|
51
|
+
module.exports.isEmptyString = function isEmptyString(str) {
|
|
52
|
+
return str.length === 0;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Returns true iff the input is an object
|
|
56
|
+
module.exports.isObject = function isObject(obj) {
|
|
57
|
+
return (obj !== null) && (typeof obj === "object") && !Array.isArray(obj);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Returns true iff the input line is blank (no content)
|
|
61
|
+
// Example: Contains nothing, whitespace, or comments
|
|
62
|
+
const blankLineRe = />|(?:<!--.*?-->)/g;
|
|
63
|
+
module.exports.isBlankLine = function isBlankLine(line) {
|
|
64
|
+
// Call to String.replace follows best practices and is not a security check
|
|
65
|
+
// False-positive for js/incomplete-multi-character-sanitization
|
|
66
|
+
return !line || !line.trim() || !line.replace(blankLineRe, "").trim();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Compare function for Array.prototype.sort for ascending order of numbers.
|
|
71
|
+
*
|
|
72
|
+
* @param {number} a First number.
|
|
73
|
+
* @param {number} b Second number.
|
|
74
|
+
* @returns {number} Positive value if a>b, negative value if b<a, 0 otherwise.
|
|
75
|
+
*/
|
|
76
|
+
module.exports.numericSortAscending = function numericSortAscending(a, b) {
|
|
77
|
+
return a - b;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Returns true iff the sorted array contains the specified element
|
|
81
|
+
module.exports.includesSorted = function includesSorted(array, element) {
|
|
82
|
+
let left = 0;
|
|
83
|
+
let right = array.length - 1;
|
|
84
|
+
while (left <= right) {
|
|
85
|
+
// eslint-disable-next-line no-bitwise
|
|
86
|
+
const mid = (left + right) >> 1;
|
|
87
|
+
if (array[mid] < element) {
|
|
88
|
+
left = mid + 1;
|
|
89
|
+
} else if (array[mid] > element) {
|
|
90
|
+
right = mid - 1;
|
|
91
|
+
} else {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Replaces the text of all properly-formatted HTML comments with whitespace
|
|
99
|
+
// This preserves the line/column information for the rest of the document
|
|
100
|
+
// Trailing whitespace is avoided with a '\' character in the last column
|
|
101
|
+
// See https://www.w3.org/TR/html5/syntax.html#comments for details
|
|
102
|
+
const htmlCommentBegin = "<!--";
|
|
103
|
+
const htmlCommentEnd = "-->";
|
|
104
|
+
module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
|
105
|
+
let i = 0;
|
|
106
|
+
while ((i = text.indexOf(htmlCommentBegin, i)) !== -1) {
|
|
107
|
+
const j = text.indexOf(htmlCommentEnd, i);
|
|
108
|
+
if (j === -1) {
|
|
109
|
+
// Un-terminated comments are treated as text
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
const comment = text.slice(i + htmlCommentBegin.length, j);
|
|
113
|
+
if ((comment.length > 0) &&
|
|
114
|
+
(comment[0] !== ">") &&
|
|
115
|
+
(comment[comment.length - 1] !== "-") &&
|
|
116
|
+
!comment.includes("--") &&
|
|
117
|
+
(text.slice(i, j + htmlCommentEnd.length)
|
|
118
|
+
.search(inlineCommentRe) === -1)) {
|
|
119
|
+
const blanks = comment
|
|
120
|
+
.replace(/[^\r\n]/g, " ")
|
|
121
|
+
.replace(/ ([\r\n])/g, "\\$1");
|
|
122
|
+
text = text.slice(0, i + htmlCommentBegin.length) +
|
|
123
|
+
blanks + text.slice(j);
|
|
124
|
+
}
|
|
125
|
+
i = j + htmlCommentEnd.length;
|
|
126
|
+
}
|
|
127
|
+
return text;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Escapes a string for use in a RegExp
|
|
131
|
+
module.exports.escapeForRegExp = function escapeForRegExp(str) {
|
|
132
|
+
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Un-escapes Markdown content (simple algorithm; not a parser)
|
|
136
|
+
const escapedMarkdownRe = /\\./g;
|
|
137
|
+
module.exports.unescapeMarkdown =
|
|
138
|
+
function unescapeMarkdown(markdown, replacement) {
|
|
139
|
+
return markdown.replace(escapedMarkdownRe, (match) => {
|
|
140
|
+
const char = match[1];
|
|
141
|
+
if ("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".includes(char)) {
|
|
142
|
+
return replacement || char;
|
|
143
|
+
}
|
|
144
|
+
return match;
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Return the string representation of a fence markup character.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} markup Fence string.
|
|
152
|
+
* @returns {string} String representation.
|
|
153
|
+
*/
|
|
154
|
+
module.exports.fencedCodeBlockStyleFor =
|
|
155
|
+
function fencedCodeBlockStyleFor(markup) {
|
|
156
|
+
switch (markup[0]) {
|
|
157
|
+
case "~":
|
|
158
|
+
return "tilde";
|
|
159
|
+
default:
|
|
160
|
+
return "backtick";
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Return the number of characters of indent for a token.
|
|
166
|
+
*
|
|
167
|
+
* @param {Object} token MarkdownItToken instance.
|
|
168
|
+
* @returns {number} Characters of indent.
|
|
169
|
+
*/
|
|
170
|
+
function indentFor(token) {
|
|
171
|
+
const line = token.line.replace(/^[\s>]*(> |>)/, "");
|
|
172
|
+
return line.length - line.trimLeft().length;
|
|
173
|
+
}
|
|
174
|
+
module.exports.indentFor = indentFor;
|
|
175
|
+
|
|
176
|
+
// Returns the heading style for a heading token
|
|
177
|
+
module.exports.headingStyleFor = function headingStyleFor(token) {
|
|
178
|
+
if ((token.map[1] - token.map[0]) === 1) {
|
|
179
|
+
if (/[^\\]#\s*$/.test(token.line)) {
|
|
180
|
+
return "atx_closed";
|
|
181
|
+
}
|
|
182
|
+
return "atx";
|
|
183
|
+
}
|
|
184
|
+
return "setext";
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Return the string representation of an unordered list marker.
|
|
189
|
+
*
|
|
190
|
+
* @param {Object} token MarkdownItToken instance.
|
|
191
|
+
* @returns {string} String representation.
|
|
192
|
+
*/
|
|
193
|
+
module.exports.unorderedListStyleFor = function unorderedListStyleFor(token) {
|
|
194
|
+
switch (token.markup) {
|
|
195
|
+
case "-":
|
|
196
|
+
return "dash";
|
|
197
|
+
case "+":
|
|
198
|
+
return "plus";
|
|
199
|
+
// case "*":
|
|
200
|
+
default:
|
|
201
|
+
return "asterisk";
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Calls the provided function for each matching token.
|
|
207
|
+
*
|
|
208
|
+
* @param {Object} params RuleParams instance.
|
|
209
|
+
* @param {string} type Token type identifier.
|
|
210
|
+
* @param {Function} handler Callback function.
|
|
211
|
+
* @returns {void}
|
|
212
|
+
*/
|
|
213
|
+
function filterTokens(params, type, handler) {
|
|
214
|
+
params.tokens.forEach(function forToken(token) {
|
|
215
|
+
if (token.type === type) {
|
|
216
|
+
handler(token);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
module.exports.filterTokens = filterTokens;
|
|
221
|
+
|
|
222
|
+
// Get line metadata array
|
|
223
|
+
module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
224
|
+
const lineMetadata = params.lines.map(function mapLine(line, index) {
|
|
225
|
+
return [ line, index, false, 0, false, false ];
|
|
226
|
+
});
|
|
227
|
+
filterTokens(params, "fence", function forToken(token) {
|
|
228
|
+
lineMetadata[token.map[0]][3] = 1;
|
|
229
|
+
lineMetadata[token.map[1] - 1][3] = -1;
|
|
230
|
+
for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
|
|
231
|
+
lineMetadata[i][2] = true;
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
filterTokens(params, "code_block", function forToken(token) {
|
|
235
|
+
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
236
|
+
lineMetadata[i][2] = true;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
filterTokens(params, "table_open", function forToken(token) {
|
|
240
|
+
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
241
|
+
lineMetadata[i][4] = true;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
filterTokens(params, "list_item_open", function forToken(token) {
|
|
245
|
+
let count = 1;
|
|
246
|
+
for (let i = token.map[0]; i < token.map[1]; i++) {
|
|
247
|
+
lineMetadata[i][5] = count;
|
|
248
|
+
count++;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
filterTokens(params, "hr", function forToken(token) {
|
|
252
|
+
lineMetadata[token.map[0]][6] = true;
|
|
253
|
+
});
|
|
254
|
+
return lineMetadata;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// Calls the provided function for each line (with context)
|
|
258
|
+
module.exports.forEachLine = function forEachLine(lineMetadata, handler) {
|
|
259
|
+
lineMetadata.forEach(function forMetadata(metadata) {
|
|
260
|
+
// Parameters: line, lineIndex, inCode, onFence, inTable, inItem, inBreak
|
|
261
|
+
handler(...metadata);
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// Returns (nested) lists as a flat array (in order)
|
|
266
|
+
module.exports.flattenLists = function flattenLists(params) {
|
|
267
|
+
const flattenedLists = [];
|
|
268
|
+
const stack = [];
|
|
269
|
+
let current = null;
|
|
270
|
+
let nesting = 0;
|
|
271
|
+
const nestingStack = [];
|
|
272
|
+
let lastWithMap = { "map": [ 0, 1 ] };
|
|
273
|
+
params.tokens.forEach(function forToken(token) {
|
|
274
|
+
if ((token.type === "bullet_list_open") ||
|
|
275
|
+
(token.type === "ordered_list_open")) {
|
|
276
|
+
// Save current context and start a new one
|
|
277
|
+
stack.push(current);
|
|
278
|
+
current = {
|
|
279
|
+
"unordered": (token.type === "bullet_list_open"),
|
|
280
|
+
"parentsUnordered": !current ||
|
|
281
|
+
(current.unordered && current.parentsUnordered),
|
|
282
|
+
"open": token,
|
|
283
|
+
"indent": indentFor(token),
|
|
284
|
+
"parentIndent": (current && current.indent) || 0,
|
|
285
|
+
"items": [],
|
|
286
|
+
"nesting": nesting,
|
|
287
|
+
"lastLineIndex": -1,
|
|
288
|
+
"insert": flattenedLists.length
|
|
289
|
+
};
|
|
290
|
+
nesting++;
|
|
291
|
+
} else if ((token.type === "bullet_list_close") ||
|
|
292
|
+
(token.type === "ordered_list_close")) {
|
|
293
|
+
// Finalize current context and restore previous
|
|
294
|
+
current.lastLineIndex = lastWithMap.map[1];
|
|
295
|
+
flattenedLists.splice(current.insert, 0, current);
|
|
296
|
+
delete current.insert;
|
|
297
|
+
current = stack.pop();
|
|
298
|
+
nesting--;
|
|
299
|
+
} else if (token.type === "list_item_open") {
|
|
300
|
+
// Add list item
|
|
301
|
+
current.items.push(token);
|
|
302
|
+
} else if (token.type === "blockquote_open") {
|
|
303
|
+
nestingStack.push(nesting);
|
|
304
|
+
nesting = 0;
|
|
305
|
+
} else if (token.type === "blockquote_close") {
|
|
306
|
+
nesting = nestingStack.pop();
|
|
307
|
+
} else if (token.map) {
|
|
308
|
+
// Track last token with map
|
|
309
|
+
lastWithMap = token;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
return flattenedLists;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// Calls the provided function for each specified inline child token
|
|
316
|
+
module.exports.forEachInlineChild =
|
|
317
|
+
function forEachInlineChild(params, type, handler) {
|
|
318
|
+
filterTokens(params, "inline", function forToken(token) {
|
|
319
|
+
token.children.forEach(function forChild(child) {
|
|
320
|
+
if (child.type === type) {
|
|
321
|
+
handler(child, token);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// Calls the provided function for each heading's content
|
|
328
|
+
module.exports.forEachHeading = function forEachHeading(params, handler) {
|
|
329
|
+
let heading = null;
|
|
330
|
+
params.tokens.forEach(function forToken(token) {
|
|
331
|
+
if (token.type === "heading_open") {
|
|
332
|
+
heading = token;
|
|
333
|
+
} else if (token.type === "heading_close") {
|
|
334
|
+
heading = null;
|
|
335
|
+
} else if ((token.type === "inline") && heading) {
|
|
336
|
+
handler(heading, token.content);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Calls the provided function for each inline code span's content.
|
|
343
|
+
*
|
|
344
|
+
* @param {string} input Markdown content.
|
|
345
|
+
* @param {Function} handler Callback function.
|
|
346
|
+
* @returns {void}
|
|
347
|
+
*/
|
|
348
|
+
function forEachInlineCodeSpan(input, handler) {
|
|
349
|
+
let currentLine = 0;
|
|
350
|
+
let currentColumn = 0;
|
|
351
|
+
let index = 0;
|
|
352
|
+
while (index < input.length) {
|
|
353
|
+
let startIndex = -1;
|
|
354
|
+
let startLine = -1;
|
|
355
|
+
let startColumn = -1;
|
|
356
|
+
let tickCount = 0;
|
|
357
|
+
let currentTicks = 0;
|
|
358
|
+
let state = "normal";
|
|
359
|
+
// Deliberate <= so trailing 0 completes the last span (ex: "text `code`")
|
|
360
|
+
// False-positive for js/index-out-of-bounds
|
|
361
|
+
for (; index <= input.length; index++) {
|
|
362
|
+
const char = input[index];
|
|
363
|
+
// Ignore backticks in link destination
|
|
364
|
+
if ((char === "[") && (state === "normal")) {
|
|
365
|
+
state = "linkTextOpen";
|
|
366
|
+
} else if ((char === "]") && (state === "linkTextOpen")) {
|
|
367
|
+
state = "linkTextClosed";
|
|
368
|
+
} else if ((char === "(") && (state === "linkTextClosed")) {
|
|
369
|
+
state = "linkDestinationOpen";
|
|
370
|
+
} else if (
|
|
371
|
+
((char === "(") && (state === "linkDestinationOpen")) ||
|
|
372
|
+
((char === ")") && (state === "linkDestinationOpen")) ||
|
|
373
|
+
(state === "linkTextClosed")) {
|
|
374
|
+
state = "normal";
|
|
375
|
+
}
|
|
376
|
+
// Parse backtick open/close
|
|
377
|
+
if ((char === "`") && (state !== "linkDestinationOpen")) {
|
|
378
|
+
// Count backticks at start or end of code span
|
|
379
|
+
currentTicks++;
|
|
380
|
+
if ((startIndex === -1) || (startColumn === -1)) {
|
|
381
|
+
startIndex = index + 1;
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
if ((startIndex >= 0) &&
|
|
385
|
+
(startColumn >= 0) &&
|
|
386
|
+
(tickCount === currentTicks)) {
|
|
387
|
+
// Found end backticks; invoke callback for code span
|
|
388
|
+
handler(
|
|
389
|
+
input.substring(startIndex, index - currentTicks),
|
|
390
|
+
startLine, startColumn, tickCount);
|
|
391
|
+
startIndex = -1;
|
|
392
|
+
startColumn = -1;
|
|
393
|
+
} else if ((startIndex >= 0) && (startColumn === -1)) {
|
|
394
|
+
// Found start backticks
|
|
395
|
+
tickCount = currentTicks;
|
|
396
|
+
startLine = currentLine;
|
|
397
|
+
startColumn = currentColumn;
|
|
398
|
+
}
|
|
399
|
+
// Not in backticks
|
|
400
|
+
currentTicks = 0;
|
|
401
|
+
}
|
|
402
|
+
if (char === "\n") {
|
|
403
|
+
// On next line
|
|
404
|
+
currentLine++;
|
|
405
|
+
currentColumn = 0;
|
|
406
|
+
} else if ((char === "\\") &&
|
|
407
|
+
((startIndex === -1) || (startColumn === -1)) &&
|
|
408
|
+
(input[index + 1] !== "\n")) {
|
|
409
|
+
// Escape character outside code, skip next
|
|
410
|
+
index++;
|
|
411
|
+
currentColumn += 2;
|
|
412
|
+
} else {
|
|
413
|
+
// On next column
|
|
414
|
+
currentColumn++;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (startIndex >= 0) {
|
|
418
|
+
// Restart loop after unmatched start backticks (ex: "`text``code``")
|
|
419
|
+
index = startIndex;
|
|
420
|
+
currentLine = startLine;
|
|
421
|
+
currentColumn = startColumn;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
module.exports.forEachInlineCodeSpan = forEachInlineCodeSpan;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Adds a generic error object via the onError callback.
|
|
429
|
+
*
|
|
430
|
+
* @param {Object} onError RuleOnError instance.
|
|
431
|
+
* @param {number} lineNumber Line number.
|
|
432
|
+
* @param {string} [detail] Error details.
|
|
433
|
+
* @param {string} [context] Error context.
|
|
434
|
+
* @param {number[]} [range] Column and length of error.
|
|
435
|
+
* @param {Object} [fixInfo] RuleOnErrorFixInfo instance.
|
|
436
|
+
* @returns {void}
|
|
437
|
+
*/
|
|
438
|
+
function addError(onError, lineNumber, detail, context, range, fixInfo) {
|
|
439
|
+
onError({
|
|
440
|
+
lineNumber,
|
|
441
|
+
detail,
|
|
442
|
+
context,
|
|
443
|
+
range,
|
|
444
|
+
fixInfo
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
module.exports.addError = addError;
|
|
448
|
+
|
|
449
|
+
// Adds an error object with details conditionally via the onError callback
|
|
450
|
+
module.exports.addErrorDetailIf = function addErrorDetailIf(
|
|
451
|
+
onError, lineNumber, expected, actual, detail, context, range, fixInfo) {
|
|
452
|
+
if (expected !== actual) {
|
|
453
|
+
addError(
|
|
454
|
+
onError,
|
|
455
|
+
lineNumber,
|
|
456
|
+
"Expected: " + expected + "; Actual: " + actual +
|
|
457
|
+
(detail ? "; " + detail : ""),
|
|
458
|
+
context,
|
|
459
|
+
range,
|
|
460
|
+
fixInfo);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// Adds an error object with context via the onError callback
|
|
465
|
+
module.exports.addErrorContext = function addErrorContext(
|
|
466
|
+
onError, lineNumber, context, left, right, range, fixInfo) {
|
|
467
|
+
if (context.length <= 30) {
|
|
468
|
+
// Nothing to do
|
|
469
|
+
} else if (left && right) {
|
|
470
|
+
context = context.substr(0, 15) + "..." + context.substr(-15);
|
|
471
|
+
} else if (right) {
|
|
472
|
+
context = "..." + context.substr(-30);
|
|
473
|
+
} else {
|
|
474
|
+
context = context.substr(0, 30) + "...";
|
|
475
|
+
}
|
|
476
|
+
addError(onError, lineNumber, null, context, range, fixInfo);
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// Returns a range object for a line by applying a RegExp
|
|
480
|
+
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
|
481
|
+
let range = null;
|
|
482
|
+
const match = line.match(regexp);
|
|
483
|
+
if (match) {
|
|
484
|
+
const column = match.index + 1;
|
|
485
|
+
const length = match[0].length;
|
|
486
|
+
range = [ column, length ];
|
|
487
|
+
}
|
|
488
|
+
return range;
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// Determines if the front matter includes a title
|
|
492
|
+
module.exports.frontMatterHasTitle =
|
|
493
|
+
function frontMatterHasTitle(frontMatterLines, frontMatterTitlePattern) {
|
|
494
|
+
const ignoreFrontMatter =
|
|
495
|
+
(frontMatterTitlePattern !== undefined) && !frontMatterTitlePattern;
|
|
496
|
+
const frontMatterTitleRe =
|
|
497
|
+
new RegExp(String(frontMatterTitlePattern || "^\\s*title\\s*[:=]"), "i");
|
|
498
|
+
return !ignoreFrontMatter &&
|
|
499
|
+
frontMatterLines.some((line) => frontMatterTitleRe.test(line));
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Returns a list of emphasis markers in code spans and links.
|
|
504
|
+
*
|
|
505
|
+
* @param {Object} params RuleParams instance.
|
|
506
|
+
* @returns {number[][]} List of markers.
|
|
507
|
+
*/
|
|
508
|
+
function emphasisMarkersInContent(params) {
|
|
509
|
+
const { lines } = params;
|
|
510
|
+
const byLine = new Array(lines.length);
|
|
511
|
+
// Search code spans
|
|
512
|
+
filterTokens(params, "inline", (token) => {
|
|
513
|
+
const { children, lineNumber, map } = token;
|
|
514
|
+
if (children.some((child) => child.type === "code_inline")) {
|
|
515
|
+
const tokenLines = lines.slice(map[0], map[1]);
|
|
516
|
+
forEachInlineCodeSpan(
|
|
517
|
+
tokenLines.join("\n"),
|
|
518
|
+
(code, lineIndex, column, tickCount) => {
|
|
519
|
+
const codeLines = code.split(newLineRe);
|
|
520
|
+
codeLines.forEach((codeLine, codeLineIndex) => {
|
|
521
|
+
let match = null;
|
|
522
|
+
while ((match = emphasisMarkersRe.exec(codeLine))) {
|
|
523
|
+
const byLineIndex = lineNumber - 1 + lineIndex + codeLineIndex;
|
|
524
|
+
const inLine = byLine[byLineIndex] || [];
|
|
525
|
+
const codeLineOffset = codeLineIndex ? 0 : column - 1 + tickCount;
|
|
526
|
+
inLine.push(codeLineOffset + match.index);
|
|
527
|
+
byLine[byLineIndex] = inLine;
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
// Search links
|
|
535
|
+
lines.forEach((tokenLine, tokenLineIndex) => {
|
|
536
|
+
let linkMatch = null;
|
|
537
|
+
while ((linkMatch = linkRe.exec(tokenLine))) {
|
|
538
|
+
let markerMatch = null;
|
|
539
|
+
while ((markerMatch = emphasisMarkersRe.exec(linkMatch[0]))) {
|
|
540
|
+
const inLine = byLine[tokenLineIndex] || [];
|
|
541
|
+
inLine.push(linkMatch.index + markerMatch.index);
|
|
542
|
+
byLine[tokenLineIndex] = inLine;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
return byLine;
|
|
547
|
+
}
|
|
548
|
+
module.exports.emphasisMarkersInContent = emphasisMarkersInContent;
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Gets the most common line ending, falling back to the platform default.
|
|
552
|
+
*
|
|
553
|
+
* @param {string} input Markdown content to analyze.
|
|
554
|
+
* @returns {string} Preferred line ending.
|
|
555
|
+
*/
|
|
556
|
+
function getPreferredLineEnding(input) {
|
|
557
|
+
let cr = 0;
|
|
558
|
+
let lf = 0;
|
|
559
|
+
let crlf = 0;
|
|
560
|
+
const endings = input.match(newLineRe) || [];
|
|
561
|
+
endings.forEach((ending) => {
|
|
562
|
+
// eslint-disable-next-line default-case
|
|
563
|
+
switch (ending) {
|
|
564
|
+
case "\r":
|
|
565
|
+
cr++;
|
|
566
|
+
break;
|
|
567
|
+
case "\n":
|
|
568
|
+
lf++;
|
|
569
|
+
break;
|
|
570
|
+
case "\r\n":
|
|
571
|
+
crlf++;
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
let preferredLineEnding = null;
|
|
576
|
+
if (!cr && !lf && !crlf) {
|
|
577
|
+
preferredLineEnding = os.EOL;
|
|
578
|
+
} else if ((lf >= crlf) && (lf >= cr)) {
|
|
579
|
+
preferredLineEnding = "\n";
|
|
580
|
+
} else if (crlf >= cr) {
|
|
581
|
+
preferredLineEnding = "\r\n";
|
|
582
|
+
} else {
|
|
583
|
+
preferredLineEnding = "\r";
|
|
584
|
+
}
|
|
585
|
+
return preferredLineEnding;
|
|
586
|
+
}
|
|
587
|
+
module.exports.getPreferredLineEnding = getPreferredLineEnding;
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Normalizes the fields of a RuleOnErrorFixInfo instance.
|
|
591
|
+
*
|
|
592
|
+
* @param {Object} fixInfo RuleOnErrorFixInfo instance.
|
|
593
|
+
* @param {number} [lineNumber] Line number.
|
|
594
|
+
* @returns {Object} Normalized RuleOnErrorFixInfo instance.
|
|
595
|
+
*/
|
|
596
|
+
function normalizeFixInfo(fixInfo, lineNumber) {
|
|
597
|
+
return {
|
|
598
|
+
"lineNumber": fixInfo.lineNumber || lineNumber,
|
|
599
|
+
"editColumn": fixInfo.editColumn || 1,
|
|
600
|
+
"deleteCount": fixInfo.deleteCount || 0,
|
|
601
|
+
"insertText": fixInfo.insertText || ""
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Fixes the specified error on a line of Markdown content.
|
|
607
|
+
*
|
|
608
|
+
* @param {string} line Line of Markdown content.
|
|
609
|
+
* @param {Object} fixInfo RuleOnErrorFixInfo instance.
|
|
610
|
+
* @param {string} lineEnding Line ending to use.
|
|
611
|
+
* @returns {string} Fixed content.
|
|
612
|
+
*/
|
|
613
|
+
function applyFix(line, fixInfo, lineEnding) {
|
|
614
|
+
const { editColumn, deleteCount, insertText } = normalizeFixInfo(fixInfo);
|
|
615
|
+
const editIndex = editColumn - 1;
|
|
616
|
+
return (deleteCount === -1) ?
|
|
617
|
+
null :
|
|
618
|
+
line.slice(0, editIndex) +
|
|
619
|
+
insertText.replace(/\n/g, lineEnding || "\n") +
|
|
620
|
+
line.slice(editIndex + deleteCount);
|
|
621
|
+
}
|
|
622
|
+
module.exports.applyFix = applyFix;
|
|
623
|
+
|
|
624
|
+
// Applies as many fixes as possible to the input lines
|
|
625
|
+
module.exports.applyFixes = function applyFixes(input, errors) {
|
|
626
|
+
const lineEnding = getPreferredLineEnding(input);
|
|
627
|
+
const lines = input.split(newLineRe);
|
|
628
|
+
// Normalize fixInfo objects
|
|
629
|
+
let fixInfos = errors
|
|
630
|
+
.filter((error) => error.fixInfo)
|
|
631
|
+
.map((error) => normalizeFixInfo(error.fixInfo, error.lineNumber));
|
|
632
|
+
// Sort bottom-to-top, line-deletes last, right-to-left, long-to-short
|
|
633
|
+
fixInfos.sort((a, b) => {
|
|
634
|
+
const aDeletingLine = (a.deleteCount === -1);
|
|
635
|
+
const bDeletingLine = (b.deleteCount === -1);
|
|
636
|
+
return (
|
|
637
|
+
(b.lineNumber - a.lineNumber) ||
|
|
638
|
+
(aDeletingLine ? 1 : (bDeletingLine ? -1 : 0)) ||
|
|
639
|
+
(b.editColumn - a.editColumn) ||
|
|
640
|
+
(b.insertText.length - a.insertText.length)
|
|
641
|
+
);
|
|
642
|
+
});
|
|
643
|
+
// Remove duplicate entries (needed for following collapse step)
|
|
644
|
+
let lastFixInfo = {};
|
|
645
|
+
fixInfos = fixInfos.filter((fixInfo) => {
|
|
646
|
+
const unique = (
|
|
647
|
+
(fixInfo.lineNumber !== lastFixInfo.lineNumber) ||
|
|
648
|
+
(fixInfo.editColumn !== lastFixInfo.editColumn) ||
|
|
649
|
+
(fixInfo.deleteCount !== lastFixInfo.deleteCount) ||
|
|
650
|
+
(fixInfo.insertText !== lastFixInfo.insertText)
|
|
651
|
+
);
|
|
652
|
+
lastFixInfo = fixInfo;
|
|
653
|
+
return unique;
|
|
654
|
+
});
|
|
655
|
+
// Collapse insert/no-delete and no-insert/delete for same line/column
|
|
656
|
+
lastFixInfo = {};
|
|
657
|
+
fixInfos.forEach((fixInfo) => {
|
|
658
|
+
if (
|
|
659
|
+
(fixInfo.lineNumber === lastFixInfo.lineNumber) &&
|
|
660
|
+
(fixInfo.editColumn === lastFixInfo.editColumn) &&
|
|
661
|
+
!fixInfo.insertText &&
|
|
662
|
+
(fixInfo.deleteCount > 0) &&
|
|
663
|
+
lastFixInfo.insertText &&
|
|
664
|
+
!lastFixInfo.deleteCount) {
|
|
665
|
+
fixInfo.insertText = lastFixInfo.insertText;
|
|
666
|
+
lastFixInfo.lineNumber = 0;
|
|
667
|
+
}
|
|
668
|
+
lastFixInfo = fixInfo;
|
|
669
|
+
});
|
|
670
|
+
fixInfos = fixInfos.filter((fixInfo) => fixInfo.lineNumber);
|
|
671
|
+
// Apply all (remaining/updated) fixes
|
|
672
|
+
let lastLineIndex = -1;
|
|
673
|
+
let lastEditIndex = -1;
|
|
674
|
+
fixInfos.forEach((fixInfo) => {
|
|
675
|
+
const { lineNumber, editColumn, deleteCount } = fixInfo;
|
|
676
|
+
const lineIndex = lineNumber - 1;
|
|
677
|
+
const editIndex = editColumn - 1;
|
|
678
|
+
if (
|
|
679
|
+
(lineIndex !== lastLineIndex) ||
|
|
680
|
+
((editIndex + deleteCount) < lastEditIndex) ||
|
|
681
|
+
(deleteCount === -1)
|
|
682
|
+
) {
|
|
683
|
+
lines[lineIndex] = applyFix(lines[lineIndex], fixInfo, lineEnding);
|
|
684
|
+
}
|
|
685
|
+
lastLineIndex = lineIndex;
|
|
686
|
+
lastEditIndex = editIndex;
|
|
687
|
+
});
|
|
688
|
+
// Return corrected input
|
|
689
|
+
return lines.filter((line) => line !== null).join(lineEnding);
|
|
690
|
+
};
|