@peaceroad/markdown-it-renderer-fence 0.8.0 → 0.9.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 +11 -6
- package/docs/code-highlighting-design.md +8 -4
- package/docs/custom-highlight-styling-guide.md +12 -6
- package/package.json +7 -5
- package/src/custom-highlight/payload-utils.js +4 -9
- package/src/custom-highlight/shiki-role-rules.js +20 -10
- package/src/custom-highlight/shiki-role.js +50 -50
- package/src/fence/render-api-constants.js +1 -1
- package/src/fence/render-api-provider.js +2 -3
- package/src/fence/render-api-renderer.js +19 -4
- package/src/fence/render-api-runtime.js +29 -10
- package/src/fence/render-markup.js +30 -15
- package/src/fence/render-shared.js +72 -22
- package/src/utils/attr-utils.js +10 -0
- package/theme/_shared/rf-core.css +7 -1
- package/theme/line-notes.css +94 -0
- package/theme/line-number.css +64 -0
- package/theme/rf-basic/README.md +31 -5
- package/theme/rf-basic/api/shiki.css +1 -1
- package/theme/rf-basic/base.css +2 -0
- package/theme/rf-basic/index.js +9 -0
- package/theme/rf-monochrome/README.md +21 -8
- package/theme/rf-monochrome/base.css +2 -0
- package/theme/rf-monochrome/index.js +4 -5
- package/theme/rf-monochrome/markup/highlightjs.css +26 -10
- package/theme/rf-monochrome/markup/shiki.css +12 -1
|
@@ -297,6 +297,20 @@ const applyCustomHighlights = (root, options = {}) => {
|
|
|
297
297
|
break
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
|
+
if (sameRefs && prevState.blockCache instanceof Map) {
|
|
301
|
+
for (const [blockId, codeEl] of blockRefs) {
|
|
302
|
+
const cached = prevState.blockCache.get(blockId)
|
|
303
|
+
const textSnapshot = typeof codeEl.textContent === 'string' ? codeEl.textContent : null
|
|
304
|
+
const boundaryRefs = getCodeBoundaryRefs(codeEl)
|
|
305
|
+
if (!cached ||
|
|
306
|
+
cached.textSnapshot !== textSnapshot ||
|
|
307
|
+
cached.firstRef !== boundaryRefs.first ||
|
|
308
|
+
cached.lastRef !== boundaryRefs.last) {
|
|
309
|
+
sameRefs = false
|
|
310
|
+
break
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
300
314
|
if (sameRefs) {
|
|
301
315
|
if (emitDiag) emitDiag({ type: 'runtime-skip', reason: 'unchanged' })
|
|
302
316
|
return { appliedBlocks: 0, appliedRanges: 0, skipped: true, reason: 'unchanged' }
|
|
@@ -308,6 +322,7 @@ const applyCustomHighlights = (root, options = {}) => {
|
|
|
308
322
|
let insertedScopeStyleMap = null
|
|
309
323
|
if (!incremental) clearAppliedHighlightNames(queryRoot)
|
|
310
324
|
const allScopeRanges = new Map()
|
|
325
|
+
const runtimeScopeNamesByVariant = new Map()
|
|
311
326
|
let appliedBlocks = 0
|
|
312
327
|
let appliedRanges = 0
|
|
313
328
|
let pendingCss = ''
|
|
@@ -342,17 +357,12 @@ const applyCustomHighlights = (root, options = {}) => {
|
|
|
342
357
|
if (payloadDigestByBlock) {
|
|
343
358
|
blockPayloadDigest = payloadDigestByBlock.get(blockId)
|
|
344
359
|
if (blockPayloadDigest === undefined) {
|
|
345
|
-
|
|
346
|
-
if (prevCached && prevCached.payloadRef === payload && prevCached.variantKey === activePayload.variantKey) {
|
|
347
|
-
blockPayloadDigest = prevCached.payloadDigest || ''
|
|
348
|
-
} else {
|
|
349
|
-
blockPayloadDigest = getCachedPayloadString(payload, payloadStringifyCache) + `|variant=${activePayload.variantKey || ''}`
|
|
350
|
-
}
|
|
360
|
+
blockPayloadDigest = getCachedPayloadString(payload, payloadStringifyCache) + `|variant=${activePayload.variantKey || ''}`
|
|
351
361
|
payloadDigestByBlock.set(blockId, blockPayloadDigest)
|
|
352
362
|
}
|
|
353
363
|
}
|
|
354
|
-
const textSnapshot = typeof codeEl.textContent === 'string' ? codeEl.textContent : null
|
|
355
|
-
const boundaryRefs = getCodeBoundaryRefs(codeEl)
|
|
364
|
+
const textSnapshot = incremental && typeof codeEl.textContent === 'string' ? codeEl.textContent : null
|
|
365
|
+
const boundaryRefs = incremental ? getCodeBoundaryRefs(codeEl) : null
|
|
356
366
|
const cached = prevBlockCache ? prevBlockCache.get(blockId) : null
|
|
357
367
|
if (cached &&
|
|
358
368
|
cached.codeEl === codeEl &&
|
|
@@ -404,6 +414,11 @@ const applyCustomHighlights = (root, options = {}) => {
|
|
|
404
414
|
const appliedNames = new Set()
|
|
405
415
|
const appliedNameList = []
|
|
406
416
|
const scopeRuntimeNames = new Array(activePayload.scopes.length)
|
|
417
|
+
let runtimeScopeNameCache = runtimeScopeNamesByVariant.get(activePayload.variantKey)
|
|
418
|
+
if (!runtimeScopeNameCache) {
|
|
419
|
+
runtimeScopeNameCache = new Map()
|
|
420
|
+
runtimeScopeNamesByVariant.set(activePayload.variantKey, runtimeScopeNameCache)
|
|
421
|
+
}
|
|
407
422
|
const scopeStyles = Array.isArray(activePayload.scopeStyles) ? activePayload.scopeStyles : null
|
|
408
423
|
const blockScopeRanges = nextBlockCache ? new Map() : null
|
|
409
424
|
const blockScopeMetaParts = nextBlockCache ? new Map() : null
|
|
@@ -448,7 +463,12 @@ const applyCustomHighlights = (root, options = {}) => {
|
|
|
448
463
|
}
|
|
449
464
|
let runtimeName = scopeRuntimeNames[scopeIdx]
|
|
450
465
|
if (!runtimeName) {
|
|
451
|
-
|
|
466
|
+
const scopeCacheKey = String(scopeName)
|
|
467
|
+
runtimeName = runtimeScopeNameCache.get(scopeCacheKey)
|
|
468
|
+
if (!runtimeName) {
|
|
469
|
+
runtimeName = getRuntimeScopeName(scopeCacheKey, activePayload.variantKey)
|
|
470
|
+
runtimeScopeNameCache.set(scopeCacheKey, runtimeName)
|
|
471
|
+
}
|
|
452
472
|
scopeRuntimeNames[scopeIdx] = runtimeName
|
|
453
473
|
if (scopeStyles) {
|
|
454
474
|
const css = styleToHighlightCss(scopeStyles[scopeIdx])
|
|
@@ -514,7 +534,6 @@ const applyCustomHighlights = (root, options = {}) => {
|
|
|
514
534
|
if (nextBlockCache) {
|
|
515
535
|
nextBlockCache.set(blockId, {
|
|
516
536
|
codeEl,
|
|
517
|
-
payloadRef: payload,
|
|
518
537
|
variantKey: activePayload.variantKey || '',
|
|
519
538
|
payloadDigest: blockPayloadDigest,
|
|
520
539
|
textSnapshot,
|
|
@@ -23,7 +23,8 @@ import {
|
|
|
23
23
|
parsePreCodeWrapper,
|
|
24
24
|
} from '../utils/pre-code-wrapper-parser.js'
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const markupHighlightPreDisabledFeatures = Object.freeze(['setLineNumber', 'line-number-skip', 'line-number-set', 'setEmphasizeLines', 'lineEndSpanThreshold', 'comment-mark', 'line-notes', 'samp'])
|
|
27
|
+
const markupPassthroughDisabledFeatures = Object.freeze([...markupHighlightPreDisabledFeatures, 'wrap', 'setPreWrapStyle'])
|
|
27
28
|
|
|
28
29
|
const renderFenceMarkup = (context, md, opt, slf) => {
|
|
29
30
|
const token = context.token
|
|
@@ -114,32 +115,45 @@ const renderFenceMarkup = (context, md, opt, slf) => {
|
|
|
114
115
|
|
|
115
116
|
const useHighlightPre = opt.useHighlightPre && hasHighlightPre
|
|
116
117
|
const needLineNumber = !useHighlightPre && opt.setLineNumber && startNumber >= 0
|
|
118
|
+
const wantsEmphasis = !useHighlightPre && opt.setEmphasizeLines && emphasizeLines.length > 0
|
|
119
|
+
const wantsAdvancedLineNumbers = needLineNumber && (lineNumberSkipValue !== undefined || lineNumberSetValue !== undefined)
|
|
120
|
+
const wantsLineNotes = !useHighlightPre && !!(lineNotes && lineNotes.length)
|
|
121
|
+
const hasCommentCandidate = !!(
|
|
122
|
+
!useHighlightPre &&
|
|
123
|
+
commentMarkValue &&
|
|
124
|
+
sourceContent.indexOf(commentMarkValue) !== -1
|
|
125
|
+
)
|
|
126
|
+
const needsMappedLineCounts = wantsAdvancedLineNumbers || wantsLineNotes || hasCommentCandidate
|
|
117
127
|
let sourceLogicalLineCount = -1
|
|
118
128
|
let highlightedLogicalLineCount = -1
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
if (wantsEmphasis || needsMappedLineCounts) highlightedLogicalLineCount = getLogicalLineCount(content)
|
|
130
|
+
if (needsMappedLineCounts) sourceLogicalLineCount = getLogicalLineCount(sourceContent)
|
|
131
|
+
const highlightedHasTrailingBreak = content.endsWith('\n') || content.endsWith('\r')
|
|
132
|
+
const needsLastLineWrapCheck = !highlightedHasTrailingBreak && (needLineNumber || wantsEmphasis || wantsLineNotes)
|
|
133
|
+
if (needsLastLineWrapCheck && highlightedLogicalLineCount < 0) highlightedLogicalLineCount = getLogicalLineCount(content)
|
|
134
|
+
if (needsLastLineWrapCheck && sourceLogicalLineCount < 0) sourceLogicalLineCount = getLogicalLineCount(sourceContent)
|
|
135
|
+
const wrapLastLine = !!(
|
|
136
|
+
needsLastLineWrapCheck &&
|
|
137
|
+
highlightedLogicalLineCount > 0 &&
|
|
138
|
+
highlightedLogicalLineCount === sourceLogicalLineCount
|
|
139
|
+
)
|
|
140
|
+
|
|
123
141
|
let normalizedEmphasis = []
|
|
124
|
-
if (
|
|
125
|
-
ensureLogicalLineCounts()
|
|
142
|
+
if (wantsEmphasis) {
|
|
126
143
|
normalizedEmphasis = normalizeEmphasisRanges(emphasizeLines, highlightedLogicalLineCount)
|
|
127
144
|
}
|
|
128
145
|
let lineNumberPlan = null
|
|
129
|
-
if (
|
|
130
|
-
ensureLogicalLineCounts()
|
|
146
|
+
if (wantsAdvancedLineNumbers) {
|
|
131
147
|
lineNumberPlan = resolveAdvancedLineNumberPlan(lineNumberSkipValue, lineNumberSetValue, sourceLogicalLineCount, highlightedLogicalLineCount)
|
|
132
148
|
}
|
|
133
149
|
let lineNotePlan = null
|
|
134
|
-
if (
|
|
135
|
-
ensureLogicalLineCounts()
|
|
150
|
+
if (wantsLineNotes) {
|
|
136
151
|
lineNotePlan = resolveLineNotesPlan(lineNotes, sourceLogicalLineCount, highlightedLogicalLineCount, lineNoteIdPrefix)
|
|
137
152
|
}
|
|
138
153
|
const needEmphasis = normalizedEmphasis.length > 0
|
|
139
154
|
const needEndSpan = opt.lineEndSpanThreshold > 0
|
|
140
155
|
|
|
141
|
-
if (
|
|
142
|
-
ensureLogicalLineCounts()
|
|
156
|
+
if (hasCommentCandidate) {
|
|
143
157
|
if (highlightedLogicalLineCount === sourceLogicalLineCount) {
|
|
144
158
|
commentLines = getCommentLineFlags(sourceContent, commentMarkValue)
|
|
145
159
|
needComment = !!commentLines
|
|
@@ -163,16 +177,17 @@ const renderFenceMarkup = (context, md, opt, slf) => {
|
|
|
163
177
|
commentLineClass,
|
|
164
178
|
lineNumberPlan,
|
|
165
179
|
lineNotePlan,
|
|
180
|
+
wrapLastLine,
|
|
166
181
|
)
|
|
167
182
|
if (timingEnabled) addTimingMs(timings, 'lineSplitMs', getNowMs() - splitStartedAt)
|
|
168
183
|
}
|
|
169
184
|
|
|
170
|
-
const tag = isSamp ? 'samp' : 'code'
|
|
185
|
+
const tag = useHighlightPre ? 'code' : (isSamp ? 'samp' : 'code')
|
|
171
186
|
const decision = {
|
|
172
187
|
renderer: 'markup',
|
|
173
188
|
useHighlightPre,
|
|
174
189
|
hasHighlightPre,
|
|
175
|
-
disabledFeatures: useHighlightPre ?
|
|
190
|
+
disabledFeatures: useHighlightPre ? markupHighlightPreDisabledFeatures : [],
|
|
176
191
|
}
|
|
177
192
|
if (timingEnabled) decision.timings = finalizeFenceTimings(timings, fenceStartedAt)
|
|
178
193
|
emitFenceDecision(opt, decision)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
appendStyleValue,
|
|
3
3
|
createAttrOrderIndexGetter,
|
|
4
|
+
escapeHtmlAttr,
|
|
4
5
|
getInfoAttr,
|
|
5
6
|
getLangFromClassAttr,
|
|
6
7
|
} from '../utils/attr-utils.js'
|
|
@@ -26,6 +27,7 @@ const preWrapStyle = 'white-space: pre-wrap; overflow-wrap: anywhere;'
|
|
|
26
27
|
const voidTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'])
|
|
27
28
|
const nonNegativeIntReg = /^\d+$/
|
|
28
29
|
const positiveIntReg = /^[1-9]\d*$/
|
|
30
|
+
const attrOrderRankCacheMaxSize = 256
|
|
29
31
|
|
|
30
32
|
const getLineNoteIdPrefix = (token, idx) => {
|
|
31
33
|
const tokenMap = token && Array.isArray(token.map) ? token.map : null
|
|
@@ -112,6 +114,7 @@ const finalizeCommonFenceOption = (opt) => {
|
|
|
112
114
|
opt._sampLangSet = new Set(sampLangs)
|
|
113
115
|
opt._attrOrderIndex = createAttrOrderIndexGetter(opt.attrsOrder || [])
|
|
114
116
|
opt._attrOrderRankCache = new Map()
|
|
117
|
+
opt.lineEndSpanClass = escapeHtmlAttr(opt.lineEndSpanClass)
|
|
115
118
|
return opt
|
|
116
119
|
}
|
|
117
120
|
|
|
@@ -254,6 +257,8 @@ const getLineNumberSkipRanges = (attrVal) => {
|
|
|
254
257
|
const normalizeEmphasisRanges = (emphasizeLines, maxLine) => {
|
|
255
258
|
const normalized = []
|
|
256
259
|
if (!emphasizeLines || !emphasizeLines.length || maxLine <= 0) return normalized
|
|
260
|
+
let previousStart = -1
|
|
261
|
+
let needsSort = false
|
|
257
262
|
for (const range of emphasizeLines) {
|
|
258
263
|
let s = range[0]
|
|
259
264
|
let e = range[1]
|
|
@@ -267,8 +272,25 @@ const normalizeEmphasisRanges = (emphasizeLines, maxLine) => {
|
|
|
267
272
|
}
|
|
268
273
|
if (s > maxLine || e < 1) continue
|
|
269
274
|
if (e > maxLine) e = maxLine
|
|
275
|
+
if (previousStart > s) needsSort = true
|
|
270
276
|
normalized.push([s, e])
|
|
277
|
+
previousStart = s
|
|
278
|
+
}
|
|
279
|
+
if (normalized.length < 2) return normalized
|
|
280
|
+
if (needsSort) normalized.sort((a, b) => a[0] - b[0] || a[1] - b[1])
|
|
281
|
+
|
|
282
|
+
let writeIndex = 1
|
|
283
|
+
for (let i = 1; i < normalized.length; i++) {
|
|
284
|
+
const previous = normalized[writeIndex - 1]
|
|
285
|
+
const current = normalized[i]
|
|
286
|
+
if (current[0] <= previous[1] + 1) {
|
|
287
|
+
if (current[1] > previous[1]) previous[1] = current[1]
|
|
288
|
+
continue
|
|
289
|
+
}
|
|
290
|
+
normalized[writeIndex] = current
|
|
291
|
+
writeIndex++
|
|
271
292
|
}
|
|
293
|
+
normalized.length = writeIndex
|
|
272
294
|
return normalized
|
|
273
295
|
}
|
|
274
296
|
|
|
@@ -428,9 +450,10 @@ const buildPreLineTags = (hidden, setNumber, lineNote) => {
|
|
|
428
450
|
}
|
|
429
451
|
}
|
|
430
452
|
|
|
431
|
-
const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, threshold, lineEndSpanClass, br, commentLines, commentClass, lineNumberPlan, lineNotePlan) => {
|
|
453
|
+
const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, threshold, lineEndSpanClass, br, commentLines, commentClass, lineNumberPlan, lineNotePlan, wrapLastLine) => {
|
|
432
454
|
const lines = content.split(br)
|
|
433
455
|
const max = lines.length
|
|
456
|
+
const logicalLineMax = wrapLastLine ? max : Math.max(max - 1, 0)
|
|
434
457
|
const lineNumberHidden = lineNumberPlan ? lineNumberPlan.hidden : null
|
|
435
458
|
const lineNumberSets = lineNumberPlan ? lineNumberPlan.sets : null
|
|
436
459
|
const lineNoteAnchors = lineNotePlan ? lineNotePlan.anchors : null
|
|
@@ -439,6 +462,7 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
|
|
|
439
462
|
let emIdx = 0
|
|
440
463
|
let emStart = -1
|
|
441
464
|
let emEnd = -1
|
|
465
|
+
let emphasisOpen = false
|
|
442
466
|
if (needEmphasis && emphasizeLines && emphasizeLines.length) {
|
|
443
467
|
emStart = emphasizeLines[0][0]
|
|
444
468
|
emEnd = emphasizeLines[0][1]
|
|
@@ -450,7 +474,8 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
|
|
|
450
474
|
|
|
451
475
|
for (let n = 0; n < max; n++) {
|
|
452
476
|
let line = lines[n]
|
|
453
|
-
const
|
|
477
|
+
const hasNextLine = n < max - 1
|
|
478
|
+
const isLogicalLine = n < logicalLineMax
|
|
454
479
|
const doComment = commentLines && commentLines[n]
|
|
455
480
|
const hidden = !!(lineNumberHidden && lineNumberHidden[n])
|
|
456
481
|
const setNumber = lineNumberSets ? lineNumberSets[n] : undefined
|
|
@@ -478,7 +503,7 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
|
|
|
478
503
|
}
|
|
479
504
|
}
|
|
480
505
|
|
|
481
|
-
if (needLineWrap &&
|
|
506
|
+
if (needLineWrap && isLogicalLine && hasNextLine) {
|
|
482
507
|
if (!hasLtChecked) {
|
|
483
508
|
hasLt = line.indexOf('<') !== -1
|
|
484
509
|
hasLtChecked = true
|
|
@@ -508,24 +533,31 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
|
|
|
508
533
|
line = `<span class="${commentClass}">` + line + closeTag
|
|
509
534
|
}
|
|
510
535
|
|
|
511
|
-
if (needLineWrap &&
|
|
536
|
+
if (needLineWrap && isLogicalLine) {
|
|
512
537
|
const lineTags = buildPreLineTags(hidden, setNumber, lineNote)
|
|
513
538
|
line = lineTags.open + line + lineTags.close
|
|
514
539
|
}
|
|
515
540
|
|
|
516
|
-
|
|
541
|
+
const opensEmphasis = needEmphasis && emIdx < emphasizeLines.length && emStart === n + 1
|
|
542
|
+
const closesEmphasis = needEmphasis && emIdx < emphasizeLines.length && emEnd === n
|
|
543
|
+
if (opensEmphasis) {
|
|
517
544
|
line = emphOpenTag + line
|
|
518
545
|
}
|
|
519
|
-
if (
|
|
546
|
+
if (closesEmphasis) {
|
|
520
547
|
line = closeTag + line
|
|
521
548
|
emIdx++
|
|
522
549
|
const nextEmphasis = emphasizeLines[emIdx] || []
|
|
523
550
|
emStart = nextEmphasis[0]
|
|
524
551
|
emEnd = nextEmphasis[1]
|
|
525
552
|
}
|
|
553
|
+
if (opensEmphasis) emphasisOpen = true
|
|
554
|
+
else if (closesEmphasis) emphasisOpen = false
|
|
526
555
|
|
|
527
556
|
lines[n] = line
|
|
528
557
|
}
|
|
558
|
+
if (emphasisOpen && logicalLineMax > 0) {
|
|
559
|
+
lines[logicalLineMax - 1] += closeTag
|
|
560
|
+
}
|
|
529
561
|
return lines.join(br)
|
|
530
562
|
}
|
|
531
563
|
|
|
@@ -560,6 +592,7 @@ const orderTokenAttrs = (token, opt) => {
|
|
|
560
592
|
let rank = rankCache.get(name)
|
|
561
593
|
if (rank === undefined) {
|
|
562
594
|
rank = opt._attrOrderIndex(name)
|
|
595
|
+
if (rankCache.size >= attrOrderRankCacheMaxSize) rankCache.clear()
|
|
563
596
|
rankCache.set(name, rank)
|
|
564
597
|
}
|
|
565
598
|
return rank
|
|
@@ -600,7 +633,17 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
600
633
|
const lineNoteIdPrefix = lineNotes && lineNotes.length ? getLineNoteIdPrefix(token, idx) : ''
|
|
601
634
|
const attrNormalizeStartedAt = timingEnabled ? getNowMs() : 0
|
|
602
635
|
|
|
603
|
-
|
|
636
|
+
const classOnlyAttr = !!(
|
|
637
|
+
token.attrs &&
|
|
638
|
+
token.attrs.length === 1 &&
|
|
639
|
+
token.attrs[0][0] === 'class'
|
|
640
|
+
)
|
|
641
|
+
if (classOnlyAttr) {
|
|
642
|
+
const classLang = getLangFromClassAttr(token.attrs[0][1], opt.langPrefix)
|
|
643
|
+
if (classLang) lang = classLang
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (token.attrs && !classOnlyAttr) {
|
|
604
647
|
const newAttrs = []
|
|
605
648
|
let dataPreStartIndex = -1
|
|
606
649
|
let dataPreEmphasisIndex = -1
|
|
@@ -616,7 +659,7 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
616
659
|
let sawEmphasisAttr = false
|
|
617
660
|
let sawLineNumberSkipAttr = false
|
|
618
661
|
let sawLineNumberSetAttr = false
|
|
619
|
-
|
|
662
|
+
let appendOrder = null
|
|
620
663
|
|
|
621
664
|
for (const attr of token.attrs) {
|
|
622
665
|
const name = attr[0]
|
|
@@ -635,7 +678,7 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
635
678
|
newAttrs.push(attr)
|
|
636
679
|
break
|
|
637
680
|
case 'data-pre-start':
|
|
638
|
-
startNumber = parseStartNumber(val) ?? -1
|
|
681
|
+
if (opt.setLineNumber) startNumber = parseStartNumber(val) ?? -1
|
|
639
682
|
startValue = val
|
|
640
683
|
dataPreStartIndex = newAttrs.length
|
|
641
684
|
newAttrs.push(attr)
|
|
@@ -643,9 +686,10 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
643
686
|
case 'line-number-start':
|
|
644
687
|
case 'start':
|
|
645
688
|
case 'pre-start':
|
|
646
|
-
startNumber = parseStartNumber(val) ?? -1
|
|
689
|
+
if (opt.setLineNumber) startNumber = parseStartNumber(val) ?? -1
|
|
647
690
|
startValue = val
|
|
648
691
|
if (!sawStartAttr) {
|
|
692
|
+
if (!appendOrder) appendOrder = []
|
|
649
693
|
appendOrder.push('start')
|
|
650
694
|
sawStartAttr = true
|
|
651
695
|
}
|
|
@@ -677,6 +721,7 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
677
721
|
if (opt.setEmphasizeLines) emphasizeLines = getEmphasizeLines(val)
|
|
678
722
|
emphasisValue = val
|
|
679
723
|
if (!sawEmphasisAttr) {
|
|
724
|
+
if (!appendOrder) appendOrder = []
|
|
680
725
|
appendOrder.push('emphasis')
|
|
681
726
|
sawEmphasisAttr = true
|
|
682
727
|
}
|
|
@@ -684,6 +729,7 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
684
729
|
case 'comment-mark':
|
|
685
730
|
commentMarkValue = val
|
|
686
731
|
if (!sawCommentMark) {
|
|
732
|
+
if (!appendOrder) appendOrder = []
|
|
687
733
|
appendOrder.push('comment')
|
|
688
734
|
sawCommentMark = true
|
|
689
735
|
}
|
|
@@ -698,6 +744,7 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
698
744
|
case 'pre-line-number-skip':
|
|
699
745
|
lineNumberSkipValue = val
|
|
700
746
|
if (!sawLineNumberSkipAttr) {
|
|
747
|
+
if (!appendOrder) appendOrder = []
|
|
701
748
|
appendOrder.push('line-number-skip')
|
|
702
749
|
sawLineNumberSkipAttr = true
|
|
703
750
|
}
|
|
@@ -706,6 +753,7 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
706
753
|
case 'pre-line-number-set':
|
|
707
754
|
lineNumberSetValue = val
|
|
708
755
|
if (!sawLineNumberSetAttr) {
|
|
756
|
+
if (!appendOrder) appendOrder = []
|
|
709
757
|
appendOrder.push('line-number-set')
|
|
710
758
|
sawLineNumberSetAttr = true
|
|
711
759
|
}
|
|
@@ -732,21 +780,23 @@ const prepareFenceRenderContext = (tokens, idx, opt) => {
|
|
|
732
780
|
if (lineNumberSkipValue !== undefined && dataPreLineNumberSkipIndex >= 0) newAttrs[dataPreLineNumberSkipIndex][1] = lineNumberSkipValue
|
|
733
781
|
if (lineNumberSetValue !== undefined && dataPreLineNumberSetIndex >= 0) newAttrs[dataPreLineNumberSetIndex][1] = lineNumberSetValue
|
|
734
782
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
783
|
+
if (appendOrder) {
|
|
784
|
+
for (const kind of appendOrder) {
|
|
785
|
+
if (kind === 'start' && dataPreStartIndex === -1 && startValue !== undefined) {
|
|
786
|
+
newAttrs.push(['data-pre-start', startValue])
|
|
787
|
+
} else if (kind === 'emphasis' && dataPreEmphasisIndex === -1 && emphasisValue !== undefined) {
|
|
788
|
+
newAttrs.push(['data-pre-emphasis', emphasisValue])
|
|
789
|
+
} else if (kind === 'comment' && dataPreCommentIndex === -1 && commentMarkValue !== undefined) {
|
|
790
|
+
newAttrs.push(['data-pre-comment-mark', commentMarkValue])
|
|
791
|
+
} else if (kind === 'line-number-skip' && dataPreLineNumberSkipIndex === -1 && lineNumberSkipValue !== undefined) {
|
|
792
|
+
newAttrs.push(['data-pre-line-number-skip', lineNumberSkipValue])
|
|
793
|
+
} else if (kind === 'line-number-set' && dataPreLineNumberSetIndex === -1 && lineNumberSetValue !== undefined) {
|
|
794
|
+
newAttrs.push(['data-pre-line-number-set', lineNumberSetValue])
|
|
795
|
+
}
|
|
746
796
|
}
|
|
747
797
|
}
|
|
748
798
|
|
|
749
|
-
if (startNumber !== -1) {
|
|
799
|
+
if (startNumber !== -1 && opt.setLineNumber) {
|
|
750
800
|
styleValue = appendStyleValue(styleValue, 'counter-set:pre-line-number ' + startNumber + ';')
|
|
751
801
|
}
|
|
752
802
|
if (styleIndex >= 0) {
|
package/src/utils/attr-utils.js
CHANGED
|
@@ -35,6 +35,15 @@ const appendStyleValue = (style, addition) => {
|
|
|
35
35
|
return base + separator + next
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
const escapeHtmlAttr = (value) => {
|
|
39
|
+
return String(value ?? '')
|
|
40
|
+
.replace(/&/g, '&')
|
|
41
|
+
.replace(/"/g, '"')
|
|
42
|
+
.replace(/'/g, ''')
|
|
43
|
+
.replace(/</g, '<')
|
|
44
|
+
.replace(/>/g, '>')
|
|
45
|
+
}
|
|
46
|
+
|
|
38
47
|
const parseHtmlAttrs = (attrText) => {
|
|
39
48
|
const attrs = []
|
|
40
49
|
if (!attrText) return attrs
|
|
@@ -113,6 +122,7 @@ const getLangFromClassAttr = (classText, langPrefix) => {
|
|
|
113
122
|
export {
|
|
114
123
|
appendStyleValue,
|
|
115
124
|
createAttrOrderIndexGetter,
|
|
125
|
+
escapeHtmlAttr,
|
|
116
126
|
getInfoAttr,
|
|
117
127
|
getLangFromClassAttr,
|
|
118
128
|
mergeAttrSets,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*! Renderer Fence shared base | MIT License */
|
|
2
2
|
|
|
3
3
|
:root {
|
|
4
|
-
color-scheme: light
|
|
4
|
+
--rf-code-color-scheme: light;
|
|
5
|
+
--rf-samp-color-scheme: dark;
|
|
5
6
|
|
|
6
7
|
--rf-code-bg: oklch(99.4% 0 0);
|
|
7
8
|
--rf-code-fg: oklch(4% 0 0);
|
|
@@ -54,6 +55,9 @@
|
|
|
54
55
|
|
|
55
56
|
@media (prefers-color-scheme: dark) {
|
|
56
57
|
:root {
|
|
58
|
+
--rf-code-color-scheme: dark;
|
|
59
|
+
--rf-samp-color-scheme: light;
|
|
60
|
+
|
|
57
61
|
--rf-code-bg: oklch(18% 0 0);
|
|
58
62
|
--rf-code-fg: oklch(95% 0 0);
|
|
59
63
|
--rf-code-muted: oklch(76% 0 0);
|
|
@@ -105,6 +109,7 @@
|
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
pre {
|
|
112
|
+
color-scheme: var(--rf-code-color-scheme);
|
|
108
113
|
margin: 0;
|
|
109
114
|
padding: 1rem;
|
|
110
115
|
overflow: auto;
|
|
@@ -135,6 +140,7 @@ pre samp {
|
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
pre:has(> samp) {
|
|
143
|
+
color-scheme: var(--rf-samp-color-scheme);
|
|
138
144
|
--rf-syntax-keyword: var(--rf-samp-syntax-keyword);
|
|
139
145
|
--rf-syntax-string: var(--rf-samp-syntax-string);
|
|
140
146
|
--rf-syntax-number: var(--rf-samp-syntax-number);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/*! Renderer Fence line-notes layout | MIT License */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--line-note-gap: 0.35rem;
|
|
5
|
+
--line-note-inline-gap: 1.25ch;
|
|
6
|
+
--line-note-width: 14rem;
|
|
7
|
+
--line-note-color: var(--rf-line-note-fg, #666);
|
|
8
|
+
--line-note-border-color: var(--rf-line-note-border, #cfcfcf);
|
|
9
|
+
--line-note-background: var(--rf-line-note-bg, rgba(255, 255, 255, 0.72));
|
|
10
|
+
--line-note-font-family: inherit;
|
|
11
|
+
--line-note-font-size: inherit;
|
|
12
|
+
--line-note-line-height: inherit;
|
|
13
|
+
--line-note-padding-block: 0.05rem;
|
|
14
|
+
--line-note-padding-inline: 0.4rem;
|
|
15
|
+
--line-note-anchor-font-size: 0.92em;
|
|
16
|
+
--line-note-anchor-line-height: 1.15;
|
|
17
|
+
--line-note-anchor-padding-block: 0;
|
|
18
|
+
--line-note-anchor-padding-inline: 0.35rem;
|
|
19
|
+
--line-note-radius: 0.25rem;
|
|
20
|
+
--line-note-label-color: var(--line-note-color);
|
|
21
|
+
--line-note-label-prefix: "line ";
|
|
22
|
+
--line-note-label-suffix: ":";
|
|
23
|
+
--line-note-anchor-padding-block-end: 1.25em;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.pre-wrapper-line-notes pre {
|
|
27
|
+
margin-bottom: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.pre-wrapper-line-notes .pre-line-note-layer {
|
|
31
|
+
display: grid;
|
|
32
|
+
gap: var(--line-note-gap);
|
|
33
|
+
margin-top: 0.65rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.pre-wrapper-line-notes .pre-line-note {
|
|
37
|
+
display: grid;
|
|
38
|
+
grid-template-columns: max-content minmax(0, 1fr);
|
|
39
|
+
align-items: start;
|
|
40
|
+
column-gap: 0.5rem;
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
color: var(--line-note-color);
|
|
43
|
+
font-family: var(--line-note-font-family);
|
|
44
|
+
font-size: var(--line-note-font-size);
|
|
45
|
+
line-height: var(--line-note-line-height);
|
|
46
|
+
vertical-align: baseline;
|
|
47
|
+
white-space: pre-wrap;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.pre-wrapper-line-notes .pre-line-note::before {
|
|
51
|
+
content: var(--line-note-label-prefix) attr(data-pre-line-note-label) var(--line-note-label-suffix);
|
|
52
|
+
display: inline-block;
|
|
53
|
+
color: var(--line-note-label-color);
|
|
54
|
+
font-weight: 700;
|
|
55
|
+
font-variant-numeric: tabular-nums;
|
|
56
|
+
white-space: nowrap;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.pre-wrapper-line-notes .pre-line-content {
|
|
60
|
+
display: inline-block;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@supports ((anchor-name: --x) and (position-anchor: --x)) {
|
|
64
|
+
.pre-wrapper-line-notes[data-pre-line-notes-layout="anchor"] {
|
|
65
|
+
position: relative;
|
|
66
|
+
anchor-scope: all;
|
|
67
|
+
padding-block-end: calc(var(--line-note-anchor-padding-block-end) + (var(--pre-line-note-overflow-lines, 0) * 1.5em));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.pre-wrapper-line-notes[data-pre-line-notes-layout="anchor"] .pre-line-note-layer {
|
|
71
|
+
margin-top: 0;
|
|
72
|
+
padding-top: 0;
|
|
73
|
+
border-top: 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.pre-wrapper-line-notes[data-pre-line-notes-layout="anchor"] .pre-line-note {
|
|
77
|
+
display: block;
|
|
78
|
+
position: absolute;
|
|
79
|
+
inset-block-start: anchor(top);
|
|
80
|
+
inset-inline-start: calc(anchor(right) + var(--line-note-inline-gap));
|
|
81
|
+
inline-size: clamp(0px, calc(100% - anchor(right) - var(--line-note-inline-gap)), var(--line-note-width));
|
|
82
|
+
padding: var(--line-note-anchor-padding-block) var(--line-note-anchor-padding-inline);
|
|
83
|
+
border: 1px solid var(--line-note-border-color);
|
|
84
|
+
border-radius: var(--line-note-radius);
|
|
85
|
+
background: var(--line-note-background);
|
|
86
|
+
font-size: var(--line-note-anchor-font-size);
|
|
87
|
+
line-height: var(--line-note-anchor-line-height);
|
|
88
|
+
transform: translateY(0.08em);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.pre-wrapper-line-notes[data-pre-line-notes-layout="anchor"] .pre-line-note::before {
|
|
92
|
+
content: none;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*! Renderer Fence line-number layout | MIT License */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--line-number-width: 2em;
|
|
5
|
+
--line-number-gap: 0.5em;
|
|
6
|
+
--line-number-rule-gap: 0.25em;
|
|
7
|
+
--line-number-color: var(--rf-line-number-fg, #666);
|
|
8
|
+
--line-number-rule-color: var(--rf-line-number-rule, #ccc);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
pre :is(code, samp)[data-pre-start] .pre-line {
|
|
12
|
+
display: block;
|
|
13
|
+
position: relative;
|
|
14
|
+
padding-inline-start: calc(var(--line-number-width) + var(--line-number-rule-gap) + var(--line-number-gap));
|
|
15
|
+
background-image: linear-gradient(var(--line-number-rule-color), var(--line-number-rule-color));
|
|
16
|
+
background-repeat: no-repeat;
|
|
17
|
+
background-size: 1px 100%;
|
|
18
|
+
background-position: calc(var(--line-number-width) + var(--line-number-rule-gap)) 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pre :is(code, samp)[data-pre-start] {
|
|
22
|
+
white-space: normal;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pre:not([data-pre-wrap]) :is(code, samp)[data-pre-start] .pre-line {
|
|
26
|
+
white-space: pre;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
pre[data-pre-wrap] :is(code, samp)[data-pre-start] .pre-line {
|
|
30
|
+
white-space: break-spaces;
|
|
31
|
+
overflow-wrap: anywhere;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pre :is(code, samp)[data-pre-start] .pre-line::before {
|
|
35
|
+
content: counter(pre-line-number);
|
|
36
|
+
position: absolute;
|
|
37
|
+
inset-inline-start: 0;
|
|
38
|
+
top: 0;
|
|
39
|
+
width: var(--line-number-width);
|
|
40
|
+
padding-inline-end: var(--line-number-rule-gap);
|
|
41
|
+
color: var(--line-number-color);
|
|
42
|
+
text-align: right;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pre :is(code, samp)[data-pre-start] .pre-line[data-pre-line-label]::before {
|
|
46
|
+
content: attr(data-pre-line-label);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pre :is(code, samp)[data-pre-start] .pre-line[data-pre-line-label-bold]::before {
|
|
50
|
+
font-weight: bold;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
pre :is(code, samp)[data-pre-start] .pre-line::after {
|
|
54
|
+
content: "";
|
|
55
|
+
counter-increment: pre-line-number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pre :is(code, samp)[data-pre-start] .pre-line.pre-line-no-number::before {
|
|
59
|
+
content: "";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pre :is(code, samp)[data-pre-start] .pre-line.pre-line-no-number::after {
|
|
63
|
+
counter-increment: none;
|
|
64
|
+
}
|