@peaceroad/markdown-it-renderer-fence 0.3.0 → 0.3.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 +1 -0
- package/index.js +134 -22
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ CSS example: <https://codepen.io/peaceroad/pen/qBGpYGK>
|
|
|
92
92
|
## Add span elements to add background color to the row ranges.
|
|
93
93
|
|
|
94
94
|
Add `em-lines` or `emphasize-lines` attribute by by adding attributes used markdown-it-attrs.
|
|
95
|
+
Open-ended ranges are supported: `-5` (1..5), `3-` (3..last), `-` (all).
|
|
95
96
|
|
|
96
97
|
~~~md
|
|
97
98
|
``` {em-lines="2,4-5"}
|
package/index.js
CHANGED
|
@@ -8,6 +8,9 @@ const emphOpenTag = '<span class="pre-lines-emphasis">'
|
|
|
8
8
|
const closeTag = '</span>'
|
|
9
9
|
const closeTagLen = closeTag.length
|
|
10
10
|
const preWrapStyle = 'white-space: pre-wrap; overflow-wrap: anywhere;'
|
|
11
|
+
const preCodeWrapperReg = /^\s*<pre\b([^>]*)>\s*<code\b([^>]*)>([\s\S]*?)<\/code>\s*<\/pre>\s*$/i
|
|
12
|
+
const htmlAttrReg = /([^\s=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g
|
|
13
|
+
const voidTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'])
|
|
11
14
|
|
|
12
15
|
const appendStyleValue = (style, addition) => {
|
|
13
16
|
if (!addition) return style
|
|
@@ -20,18 +23,75 @@ const appendStyleValue = (style, addition) => {
|
|
|
20
23
|
return base + separator + next
|
|
21
24
|
}
|
|
22
25
|
|
|
26
|
+
const parseHtmlAttrs = (attrText) => {
|
|
27
|
+
const attrs = []
|
|
28
|
+
if (!attrText) return attrs
|
|
29
|
+
htmlAttrReg.lastIndex = 0
|
|
30
|
+
let match
|
|
31
|
+
while ((match = htmlAttrReg.exec(attrText)) !== null) {
|
|
32
|
+
const name = match[1]
|
|
33
|
+
const val = match[2] ?? match[3] ?? match[4] ?? ''
|
|
34
|
+
attrs.push([name, val])
|
|
35
|
+
}
|
|
36
|
+
return attrs
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const mergeAttrSets = (target, source) => {
|
|
40
|
+
if (!source || source.length === 0) return
|
|
41
|
+
const index = new Map()
|
|
42
|
+
for (let i = 0; i < target.length; i++) index.set(target[i][0], i)
|
|
43
|
+
for (const [name, val] of source) {
|
|
44
|
+
const idx = index.get(name)
|
|
45
|
+
if (name === 'class') {
|
|
46
|
+
if (idx === undefined) {
|
|
47
|
+
target.push([name, val])
|
|
48
|
+
index.set(name, target.length - 1)
|
|
49
|
+
} else if (val) {
|
|
50
|
+
const existing = target[idx][1]
|
|
51
|
+
target[idx][1] = existing ? existing + ' ' + val : val
|
|
52
|
+
}
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
if (name === 'style') {
|
|
56
|
+
if (idx === undefined) {
|
|
57
|
+
target.push([name, val])
|
|
58
|
+
index.set(name, target.length - 1)
|
|
59
|
+
} else {
|
|
60
|
+
target[idx][1] = appendStyleValue(target[idx][1], val)
|
|
61
|
+
}
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
if (idx === undefined) {
|
|
65
|
+
target.push([name, val])
|
|
66
|
+
index.set(name, target.length - 1)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
23
71
|
const getEmphasizeLines = (attrVal) => {
|
|
24
72
|
const lines = []
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
73
|
+
for (const range of attrVal.split(',')) {
|
|
74
|
+
const part = range.trim()
|
|
75
|
+
if (!part) continue
|
|
76
|
+
const dash = part.indexOf('-')
|
|
77
|
+
if (dash > -1) {
|
|
78
|
+
const left = part.slice(0, dash).trim()
|
|
79
|
+
const right = part.slice(dash + 1).trim()
|
|
80
|
+
const s = left ? parseInt(left, 10) : null
|
|
81
|
+
const e = right ? parseInt(right, 10) : null
|
|
82
|
+
if (s !== null && (!Number.isFinite(s) || s <= 0)) continue
|
|
83
|
+
if (e !== null && (!Number.isFinite(e) || e <= 0)) continue
|
|
84
|
+
if (s === null && e === null) {
|
|
85
|
+
lines.push([null, null])
|
|
86
|
+
} else {
|
|
87
|
+
lines.push([s, e])
|
|
88
|
+
}
|
|
30
89
|
} else {
|
|
31
|
-
s = parseInt(
|
|
90
|
+
const s = parseInt(part, 10)
|
|
91
|
+
if (!Number.isFinite(s) || s <= 0) continue
|
|
32
92
|
lines.push([s, s])
|
|
33
93
|
}
|
|
34
|
-
}
|
|
94
|
+
}
|
|
35
95
|
return lines
|
|
36
96
|
}
|
|
37
97
|
|
|
@@ -39,8 +99,33 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
|
|
|
39
99
|
const lines = content.split(br)
|
|
40
100
|
const len = lines.length
|
|
41
101
|
const endSpanTag = needEndSpan ? `<span class="${lineEndSpanClass}"></span>` : ''
|
|
102
|
+
const maxLine = len - (lines[len - 1] === '' ? 1 : 0)
|
|
42
103
|
let emIdx = 0
|
|
43
|
-
let
|
|
104
|
+
let emStart
|
|
105
|
+
let emEnd
|
|
106
|
+
let doEmphasis = false
|
|
107
|
+
if (needEmphasis && maxLine > 0) {
|
|
108
|
+
const normalized = []
|
|
109
|
+
for (const range of emphasizeLines) {
|
|
110
|
+
let [s, e] = range
|
|
111
|
+
if (s == null) s = 1
|
|
112
|
+
if (e == null) e = maxLine
|
|
113
|
+
if (!Number.isFinite(s) || !Number.isFinite(e) || s <= 0 || e <= 0) continue
|
|
114
|
+
if (s > e) {
|
|
115
|
+
const tmp = s
|
|
116
|
+
s = e
|
|
117
|
+
e = tmp
|
|
118
|
+
}
|
|
119
|
+
if (s > maxLine || e < 1) continue
|
|
120
|
+
if (e > maxLine) e = maxLine
|
|
121
|
+
normalized.push([s, e])
|
|
122
|
+
}
|
|
123
|
+
if (normalized.length > 0) {
|
|
124
|
+
doEmphasis = true
|
|
125
|
+
emphasizeLines = normalized
|
|
126
|
+
;[emStart, emEnd] = emphasizeLines[0]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
44
129
|
for (let n = 0; n < len; n++) {
|
|
45
130
|
let line = lines[n]
|
|
46
131
|
|
|
@@ -69,10 +154,11 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
|
|
|
69
154
|
let match
|
|
70
155
|
while ((match = tagReg.exec(line)) !== null) {
|
|
71
156
|
const [fullMatch, tagName] = match
|
|
157
|
+
const tagNameLower = tagName.toLowerCase()
|
|
72
158
|
if (fullMatch.startsWith('</')) {
|
|
73
|
-
if (tagStack[tagStack.length-1] ===
|
|
74
|
-
} else if (!fullMatch.endsWith('/>')) {
|
|
75
|
-
tagStack.push(
|
|
159
|
+
if (tagStack[tagStack.length-1] === tagNameLower) tagStack.pop()
|
|
160
|
+
} else if (!fullMatch.endsWith('/>') && !voidTags.has(tagNameLower)) {
|
|
161
|
+
tagStack.push(tagNameLower)
|
|
76
162
|
}
|
|
77
163
|
}
|
|
78
164
|
for (let i = tagStack.length-1; i >= 0; i--) {
|
|
@@ -84,7 +170,7 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
|
|
|
84
170
|
line = preLineTag + line + closeTag
|
|
85
171
|
}
|
|
86
172
|
|
|
87
|
-
if (
|
|
173
|
+
if (doEmphasis) {
|
|
88
174
|
if (emStart === n + 1) line = emphOpenTag + line
|
|
89
175
|
if (emEnd === n) {
|
|
90
176
|
line = closeTag + line
|
|
@@ -258,15 +344,6 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
|
258
344
|
if (wrapEnabled) preWrapValue = 'true'
|
|
259
345
|
token.attrs = newAttrs
|
|
260
346
|
}
|
|
261
|
-
orderTokenAttrs(token, opt)
|
|
262
|
-
let preAttrs = ''
|
|
263
|
-
if (preWrapValue !== undefined) {
|
|
264
|
-
const escapeHtml = md.utils.escapeHtml
|
|
265
|
-
preAttrs = ' data-pre-wrap="' + escapeHtml(preWrapValue) + '"'
|
|
266
|
-
if (wrapEnabled && opt.setPreWrapStyle !== false) {
|
|
267
|
-
preAttrs += ' style="' + escapeHtml(preWrapStyle) + '"'
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
347
|
|
|
271
348
|
if (opt.setHighlight && md.options.highlight) {
|
|
272
349
|
if (lang && lang !== 'samp' ) {
|
|
@@ -278,6 +355,41 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
|
278
355
|
content = md.utils.escapeHtml(token.content)
|
|
279
356
|
}
|
|
280
357
|
|
|
358
|
+
let preAttrsFromHighlight
|
|
359
|
+
if (content.indexOf('<pre') !== -1) {
|
|
360
|
+
const preMatch = content.match(preCodeWrapperReg)
|
|
361
|
+
if (preMatch) {
|
|
362
|
+
preAttrsFromHighlight = parseHtmlAttrs(preMatch[1])
|
|
363
|
+
const codeAttrsFromHighlight = parseHtmlAttrs(preMatch[2])
|
|
364
|
+
content = preMatch[3]
|
|
365
|
+
if (codeAttrsFromHighlight.length) {
|
|
366
|
+
if (!token.attrs) token.attrs = []
|
|
367
|
+
mergeAttrSets(token.attrs, codeAttrsFromHighlight)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
orderTokenAttrs(token, opt)
|
|
372
|
+
|
|
373
|
+
let preAttrs = preAttrsFromHighlight ? preAttrsFromHighlight.slice() : []
|
|
374
|
+
if (preWrapValue !== undefined) {
|
|
375
|
+
const idx = preAttrs.findIndex(attr => attr[0] === 'data-pre-wrap')
|
|
376
|
+
if (idx === -1) {
|
|
377
|
+
preAttrs.push(['data-pre-wrap', preWrapValue])
|
|
378
|
+
} else {
|
|
379
|
+
preAttrs[idx][1] = preWrapValue
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (wrapEnabled && opt.setPreWrapStyle !== false) {
|
|
383
|
+
const idx = preAttrs.findIndex(attr => attr[0] === 'style')
|
|
384
|
+
if (idx === -1) {
|
|
385
|
+
preAttrs.push(['style', preWrapStyle])
|
|
386
|
+
} else {
|
|
387
|
+
preAttrs[idx][1] = appendStyleValue(preAttrs[idx][1], preWrapStyle)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (preAttrs.length) orderTokenAttrs({ attrs: preAttrs }, opt)
|
|
391
|
+
const preAttrsText = preAttrs.length ? slf.renderAttrs({ attrs: preAttrs }) : ''
|
|
392
|
+
|
|
281
393
|
const needLineNumber = opt.setLineNumber && startNumber >= 0
|
|
282
394
|
const needEmphasis = opt.setEmphasizeLines && emphasizeLines.length > 0
|
|
283
395
|
const needEndSpan = opt.setLineEndSpan > 0
|
|
@@ -288,7 +400,7 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
|
|
|
288
400
|
}
|
|
289
401
|
|
|
290
402
|
const tag = opt._sampReg.test(lang) ? 'samp' : 'code'
|
|
291
|
-
return `<pre${
|
|
403
|
+
return `<pre${preAttrsText}><${tag}${slf.renderAttrs(token)}>${content}</${tag}></pre>\n`
|
|
292
404
|
}
|
|
293
405
|
|
|
294
406
|
const mditRendererFence = (md, option) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peaceroad/markdown-it-renderer-fence",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.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": [
|
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
"repository": "https://github.com/peaceroad/p7d-markdown-it-renderer-fence",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@peaceroad/markdown-it-figure-with-p-caption": "^0.
|
|
19
|
+
"@peaceroad/markdown-it-figure-with-p-caption": "^0.15.1",
|
|
20
|
+
"highlight.js": "^11.11.1",
|
|
20
21
|
"markdown-it": "^14.1.0",
|
|
21
22
|
"markdown-it-attrs": "^4.3.1",
|
|
22
|
-
"
|
|
23
|
+
"shiki": "^3.21.0"
|
|
23
24
|
}
|
|
24
25
|
}
|