@peaceroad/markdown-it-renderer-fence 0.4.0 → 0.4.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/README.md +9 -4
- package/index.js +41 -17
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ $ pwd
|
|
|
71
71
|
## Add span elements to display line number.
|
|
72
72
|
|
|
73
73
|
Add `start` or `data-pre-start` attribute by adding attributes used markdown-it-attrs.
|
|
74
|
+
Line numbering is enabled only when `start` is a non-negative integer (`0`, `1`, `2`, ...).
|
|
74
75
|
|
|
75
76
|
~~~md
|
|
76
77
|
```js {start="1"}
|
|
@@ -89,9 +90,11 @@ md.render('Nyaan')
|
|
|
89
90
|
|
|
90
91
|
CSS example: <https://codepen.io/peaceroad/pen/qBGpYGK>
|
|
91
92
|
|
|
93
|
+
If `start` is empty or not a non-negative integer (for example `start=""`, `start="abc"`, `start="1.5"`), the attribute is kept as `data-pre-start` but line-number wrapping/counter style is not enabled.
|
|
94
|
+
|
|
92
95
|
## Add span elements to add background color to the row ranges.
|
|
93
96
|
|
|
94
|
-
Add `em-lines` or `emphasize-lines` attribute by
|
|
97
|
+
Add `em-lines` or `emphasize-lines` attribute by adding attributes used markdown-it-attrs.
|
|
95
98
|
Open-ended ranges are supported: `-5` (1..5), `3-` (3..last), `-` (all).
|
|
96
99
|
|
|
97
100
|
~~~md
|
|
@@ -151,6 +154,8 @@ echo 1
|
|
|
151
154
|
~~~
|
|
152
155
|
|
|
153
156
|
Note: this feature relies on line splitting and is disabled when `useHighlightPre` is true and `<pre><code>` is provided by the highlighter.
|
|
157
|
+
If a highlighter changes logical line count (for example by injecting extra lines), comment-line marking is skipped to avoid wrong line mapping.
|
|
158
|
+
CRLF/LF mixed newlines are supported for logical line-count checks.
|
|
154
159
|
|
|
155
160
|
|
|
156
161
|
## Options
|
|
@@ -165,8 +170,8 @@ The following options can be specified when initializing the plugin:
|
|
|
165
170
|
- setLineEndSpan: alias for lineEndSpanThreshold.
|
|
166
171
|
- lineEndSpanClass: default 'pre-lineend-spacer' — CSS class for end-of-line span.
|
|
167
172
|
- setPreWrapStyle: default true — include inline pre-wrap styles on `<pre>` when wrap is enabled.
|
|
168
|
-
- useHighlightPre: default false — if highlight returns `<pre><code>`, keep that wrapper and skip line-splitting features; attributes are still merged.
|
|
169
|
-
|
|
170
|
-
When `useHighlightPre` is true and the highlight output contains `<pre><code>`, line-splitting features are disabled (setLineNumber, setEmphasizeLines, lineEndSpanThreshold, comment-line).
|
|
171
173
|
- sampLang: default 'shell,console' — comma-separated list of languages (in addition to 'samp') that will be rendered using `<samp>`.
|
|
172
174
|
- langPrefix: default md.options.langPrefix || 'language-' — prefix for language class on code blocks.
|
|
175
|
+
- useHighlightPre: default false — if highlight returns `<pre><code>`, keep that wrapper and skip line-splitting features; attributes are still merged.
|
|
176
|
+
|
|
177
|
+
When `useHighlightPre` is true and the highlight output contains `<pre><code>`, line-splitting features are disabled (setLineNumber, setEmphasizeLines, lineEndSpanThreshold, comment-line). In this mode `<samp>` conversion is not possible, so avoid `useHighlightPre` when you need samp output.
|
package/index.js
CHANGED
|
@@ -12,6 +12,8 @@ const preWrapStyle = 'white-space: pre-wrap; overflow-wrap: anywhere;'
|
|
|
12
12
|
const preCodeWrapperReg = /^\s*<pre\b([^>]*)>\s*<code\b([^>]*)>([\s\S]*?)<\/code>\s*<\/pre>\s*$/i
|
|
13
13
|
const htmlAttrReg = /([^\s=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g
|
|
14
14
|
const voidTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'])
|
|
15
|
+
const nonNegativeIntReg = /^\d+$/
|
|
16
|
+
const lineBreakReg = /\r\n|\n|\r/
|
|
15
17
|
|
|
16
18
|
const appendStyleValue = (style, addition) => {
|
|
17
19
|
if (!addition) return style
|
|
@@ -69,6 +71,21 @@ const mergeAttrSets = (target, source) => {
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
const parseStartNumber = (val) => {
|
|
75
|
+
if (val == null) return null
|
|
76
|
+
const text = String(val).trim()
|
|
77
|
+
if (!nonNegativeIntReg.test(text)) return null
|
|
78
|
+
const n = Number(text)
|
|
79
|
+
if (!Number.isSafeInteger(n)) return null
|
|
80
|
+
return n
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const getLogicalLineCount = (text) => {
|
|
84
|
+
const lines = text.split(lineBreakReg)
|
|
85
|
+
if (lines.length === 0) return 0
|
|
86
|
+
return lines[lines.length - 1] === '' ? lines.length - 1 : lines.length
|
|
87
|
+
}
|
|
88
|
+
|
|
72
89
|
const getEmphasizeLines = (attrVal) => {
|
|
73
90
|
const lines = []
|
|
74
91
|
for (const range of attrVal.split(',')) {
|
|
@@ -97,7 +114,7 @@ const getEmphasizeLines = (attrVal) => {
|
|
|
97
114
|
}
|
|
98
115
|
|
|
99
116
|
const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, threshold, lineEndSpanClass, br, commentLines, commentClass) => {
|
|
100
|
-
const lines = content.split(
|
|
117
|
+
const lines = content.split(lineBreakReg)
|
|
101
118
|
const len = lines.length
|
|
102
119
|
const endSpanTag = needEndSpan ? `<span class="${lineEndSpanClass}"></span>` : ''
|
|
103
120
|
const needComment = !!(commentLines && commentClass)
|
|
@@ -233,16 +250,14 @@ const orderTokenAttrs = (token, opt) => {
|
|
|
233
250
|
const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
234
251
|
const token = tokens[idx]
|
|
235
252
|
let content = token.content
|
|
236
|
-
const
|
|
237
|
-
const match = info.match(infoReg)
|
|
253
|
+
const match = token.info.trim().match(infoReg)
|
|
238
254
|
let lang = match ? match[1] : ''
|
|
239
255
|
|
|
240
256
|
if (match && match[2]) {
|
|
241
257
|
getInfoAttr(match[2]).forEach(([name, val]) => token.attrJoin(name, val))
|
|
242
258
|
}
|
|
243
|
-
let langClass = ''
|
|
244
259
|
if (lang && lang !== 'samp') {
|
|
245
|
-
langClass = opt.langPrefix + lang
|
|
260
|
+
const langClass = opt.langPrefix + lang
|
|
246
261
|
const existingClass = token.attrGet('class')
|
|
247
262
|
token.attrSet('class', existingClass ? langClass + ' ' + existingClass : langClass)
|
|
248
263
|
}
|
|
@@ -286,14 +301,14 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
|
286
301
|
newAttrs.push(attr)
|
|
287
302
|
break
|
|
288
303
|
case 'data-pre-start':
|
|
289
|
-
startNumber =
|
|
304
|
+
startNumber = parseStartNumber(val) ?? -1
|
|
290
305
|
startValue = val
|
|
291
306
|
dataPreStartIndex = newAttrs.length
|
|
292
307
|
newAttrs.push(attr)
|
|
293
308
|
break
|
|
294
309
|
case 'start':
|
|
295
310
|
case 'pre-start':
|
|
296
|
-
startNumber =
|
|
311
|
+
startNumber = parseStartNumber(val) ?? -1
|
|
297
312
|
startValue = val
|
|
298
313
|
if (!sawStartAttr) {
|
|
299
314
|
appendOrder.push('start')
|
|
@@ -311,7 +326,7 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
|
311
326
|
break
|
|
312
327
|
case 'em-lines':
|
|
313
328
|
case 'emphasize-lines':
|
|
314
|
-
emphasizeLines = getEmphasizeLines(val)
|
|
329
|
+
if (opt.setEmphasizeLines) emphasizeLines = getEmphasizeLines(val)
|
|
315
330
|
emphasisValue = val
|
|
316
331
|
if (!sawEmphasisAttr) {
|
|
317
332
|
appendOrder.push('emphasis')
|
|
@@ -379,15 +394,7 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
|
379
394
|
const isSamp = opt._sampReg.test(lang)
|
|
380
395
|
let commentLines
|
|
381
396
|
let needComment = false
|
|
382
|
-
|
|
383
|
-
const rawLines = token.content.split(/\r?\n/)
|
|
384
|
-
commentLines = new Array(rawLines.length)
|
|
385
|
-
for (let i = 0; i < rawLines.length; i++) {
|
|
386
|
-
const isComment = rawLines[i].trimStart().startsWith(commentLineValue)
|
|
387
|
-
commentLines[i] = isComment
|
|
388
|
-
if (isComment) needComment = true
|
|
389
|
-
}
|
|
390
|
-
}
|
|
397
|
+
let sourceLogicalLineCount = -1
|
|
391
398
|
|
|
392
399
|
if (opt.setHighlight && md.options.highlight) {
|
|
393
400
|
if (lang && lang !== 'samp' ) {
|
|
@@ -444,9 +451,26 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
|
444
451
|
const needEmphasis = opt.setEmphasizeLines && emphasizeLines.length > 0
|
|
445
452
|
const needEndSpan = opt.lineEndSpanThreshold > 0
|
|
446
453
|
const useHighlightPre = opt.useHighlightPre && hasHighlightPre
|
|
454
|
+
if (!useHighlightPre && commentLineValue) {
|
|
455
|
+
const rawLines = token.content.split(lineBreakReg)
|
|
456
|
+
sourceLogicalLineCount = getLogicalLineCount(token.content)
|
|
457
|
+
commentLines = new Array(rawLines.length)
|
|
458
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
459
|
+
const isComment = rawLines[i].trimStart().startsWith(commentLineValue)
|
|
460
|
+
commentLines[i] = isComment
|
|
461
|
+
if (isComment) needComment = true
|
|
462
|
+
}
|
|
463
|
+
}
|
|
447
464
|
if (!useHighlightPre && (needLineNumber || needEmphasis || needEndSpan || needComment)) {
|
|
448
465
|
const nlIndex = content.indexOf('\n')
|
|
449
466
|
const br = nlIndex > 0 && content[nlIndex - 1] === '\r' ? '\r\n' : '\n'
|
|
467
|
+
if (needComment && sourceLogicalLineCount >= 0) {
|
|
468
|
+
const highlightedLogicalLineCount = getLogicalLineCount(content)
|
|
469
|
+
if (highlightedLogicalLineCount !== sourceLogicalLineCount) {
|
|
470
|
+
needComment = false
|
|
471
|
+
commentLines = undefined
|
|
472
|
+
}
|
|
473
|
+
}
|
|
450
474
|
content = splitFenceBlockToLines(content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, opt.lineEndSpanThreshold, opt.lineEndSpanClass, br, commentLines, commentLineClass)
|
|
451
475
|
}
|
|
452
476
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peaceroad/markdown-it-renderer-fence",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "In code blocks, the `<samp>` tag is used with the language keywords `samp`, `shell`, and `console`, and provides an option to display line numbers.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
],
|
|
11
11
|
"type": "module",
|
|
12
12
|
"scripts": {
|
|
13
|
-
"test": "node test/test.js"
|
|
13
|
+
"test": "node test/test.js",
|
|
14
|
+
"test:performance": "node test/performance/benchmark.js",
|
|
15
|
+
"test:performance:highlighter": "node test/performance/highlighter-benchmark.js"
|
|
14
16
|
},
|
|
15
17
|
"author": "peaceroad <peaceroad@gmail.com>",
|
|
16
18
|
"repository": "https://github.com/peaceroad/p7d-markdown-it-renderer-fence",
|