@peaceroad/markdown-it-renderer-fence 0.3.1 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +24 -1
  2. package/index.js +65 -8
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -133,6 +133,25 @@ const longLine = 'This line is long but wraps.'
133
133
 
134
134
  When `setPreWrapStyle` is set to false, only `data-pre-wrap="true"` is added and the inline style is omitted.
135
135
 
136
+ ## Mark comment lines in code blocks
137
+
138
+ Add `comment-line` attribute to mark comment lines. Lines that start with the given marker (after leading whitespace) are wrapped with `<span class="pre-comment-line">`.
139
+
140
+ ~~~md
141
+ ```samp {comment-line="#"}
142
+ # comment
143
+ echo 1
144
+ ```
145
+ ~~~
146
+
147
+ ~~~html
148
+ <pre><samp data-pre-comment-line="#"><span class="pre-comment-line"># comment</span>
149
+ echo 1
150
+ </samp></pre>
151
+ ~~~
152
+
153
+ Note: this feature relies on line splitting and is disabled when `useHighlightPre` is true and `<pre><code>` is provided by the highlighter.
154
+
136
155
 
137
156
  ## Options
138
157
 
@@ -142,8 +161,12 @@ The following options can be specified when initializing the plugin:
142
161
  - setHighlight: default true — enable calling highlight function (e.g., highlight.js).
143
162
  - setLineNumber: default true — wrap lines in spans for line numbers.
144
163
  - setEmphasizeLines: default true — enable emphasis based on emphasize-lines attribute.
145
- - setLineEndSpan: default 0 — character count threshold to append end-of-line span (0 to disable).
164
+ - lineEndSpanThreshold: default 0 — character count threshold to append end-of-line span (0 to disable).
165
+ - setLineEndSpan: alias for lineEndSpanThreshold.
146
166
  - lineEndSpanClass: default 'pre-lineend-spacer' — CSS class for end-of-line span.
147
167
  - 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).
148
171
  - sampLang: default 'shell,console' — comma-separated list of languages (in addition to 'samp') that will be rendered using `<samp>`.
149
172
  - langPrefix: default md.options.langPrefix || 'language-' — prefix for language class on code blocks.
package/index.js CHANGED
@@ -5,6 +5,7 @@ const interAttrsSpaceReg = / +/
5
5
  const tagReg = /<\/?([A-Za-z][A-Za-z0-9-]*)(?:\s+[^>]*?)?\/?\s*>/g
6
6
  const preLineTag = '<span class="pre-line">'
7
7
  const emphOpenTag = '<span class="pre-lines-emphasis">'
8
+ const commentLineClass = 'pre-comment-line'
8
9
  const closeTag = '</span>'
9
10
  const closeTagLen = closeTag.length
10
11
  const preWrapStyle = 'white-space: pre-wrap; overflow-wrap: anywhere;'
@@ -95,10 +96,11 @@ const getEmphasizeLines = (attrVal) => {
95
96
  return lines
96
97
  }
97
98
 
98
- const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, threshold, lineEndSpanClass, br) => {
99
+ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, threshold, lineEndSpanClass, br, commentLines, commentClass) => {
99
100
  const lines = content.split(br)
100
101
  const len = lines.length
101
102
  const endSpanTag = needEndSpan ? `<span class="${lineEndSpanClass}"></span>` : ''
103
+ const needComment = !!(commentLines && commentClass)
102
104
  const maxLine = len - (lines[len - 1] === '' ? 1 : 0)
103
105
  let emIdx = 0
104
106
  let emStart
@@ -167,6 +169,13 @@ const splitFenceBlockToLines = (content, emphasizeLines, needLineNumber, needEmp
167
169
  lines[n+1] = `<${tagName}>` + (lines[n+1]||'')
168
170
  }
169
171
  }
172
+ }
173
+
174
+ if (needComment && commentLines[n]) {
175
+ line = `<span class="${commentClass}">` + line + closeTag
176
+ }
177
+
178
+ if (needLineNumber && n !== len - 1) {
170
179
  line = preLineTag + line + closeTag
171
180
  }
172
181
 
@@ -242,15 +251,18 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
242
251
  let emphasizeLines = []
243
252
  let wrapEnabled = false
244
253
  let preWrapValue
254
+ let commentLineValue
245
255
 
246
256
  if (token.attrs) {
247
257
  const newAttrs = []
248
258
  let dataPreStartIndex = -1
249
259
  let dataPreEmphasisIndex = -1
250
260
  let styleIndex = -1
261
+ let dataPreCommentIndex = -1
251
262
  let startValue
252
263
  let emphasisValue
253
264
  let styleValue
265
+ let sawCommentLine = false
254
266
  let sawStartAttr = false
255
267
  let sawEmphasisAttr = false
256
268
  const appendOrder = []
@@ -292,6 +304,11 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
292
304
  dataPreEmphasisIndex = newAttrs.length
293
305
  newAttrs.push(attr)
294
306
  break
307
+ case 'data-pre-comment-line':
308
+ dataPreCommentIndex = newAttrs.length
309
+ commentLineValue = val
310
+ newAttrs.push(attr)
311
+ break
295
312
  case 'em-lines':
296
313
  case 'emphasize-lines':
297
314
  emphasizeLines = getEmphasizeLines(val)
@@ -301,6 +318,13 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
301
318
  sawEmphasisAttr = true
302
319
  }
303
320
  break
321
+ case 'comment-line':
322
+ commentLineValue = val
323
+ if (!sawCommentLine) {
324
+ appendOrder.push('comment')
325
+ sawCommentLine = true
326
+ }
327
+ break
304
328
  case 'data-pre-wrap':
305
329
  preWrapValue = val
306
330
  if (val === '' || val === 'true') wrapEnabled = true
@@ -322,6 +346,9 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
322
346
  if (emphasisValue !== undefined && dataPreEmphasisIndex >= 0) {
323
347
  newAttrs[dataPreEmphasisIndex][1] = emphasisValue
324
348
  }
349
+ if (commentLineValue !== undefined && dataPreCommentIndex >= 0) {
350
+ newAttrs[dataPreCommentIndex][1] = commentLineValue
351
+ }
325
352
  for (const kind of appendOrder) {
326
353
  if (kind === 'start') {
327
354
  if (dataPreStartIndex === -1 && startValue !== undefined) {
@@ -331,6 +358,10 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
331
358
  if (dataPreEmphasisIndex === -1 && emphasisValue !== undefined) {
332
359
  newAttrs.push(['data-pre-emphasis', emphasisValue])
333
360
  }
361
+ } else if (kind === 'comment') {
362
+ if (dataPreCommentIndex === -1 && commentLineValue !== undefined) {
363
+ newAttrs.push(['data-pre-comment-line', commentLineValue])
364
+ }
334
365
  }
335
366
  }
336
367
  if (startNumber !== -1) {
@@ -345,6 +376,19 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
345
376
  token.attrs = newAttrs
346
377
  }
347
378
 
379
+ const isSamp = opt._sampReg.test(lang)
380
+ let commentLines
381
+ let needComment = false
382
+ if (commentLineValue) {
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
+ }
391
+
348
392
  if (opt.setHighlight && md.options.highlight) {
349
393
  if (lang && lang !== 'samp' ) {
350
394
  content = md.options.highlight(content, lang)
@@ -356,9 +400,12 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
356
400
  }
357
401
 
358
402
  let preAttrsFromHighlight
359
- if (content.indexOf('<pre') !== -1) {
403
+ let hasHighlightPre = false
404
+ const hasPreTag = (content.indexOf('<pre') !== -1 || content.indexOf('<PRE') !== -1)
405
+ if (hasPreTag) {
360
406
  const preMatch = content.match(preCodeWrapperReg)
361
407
  if (preMatch) {
408
+ hasHighlightPre = true
362
409
  preAttrsFromHighlight = parseHtmlAttrs(preMatch[1])
363
410
  const codeAttrsFromHighlight = parseHtmlAttrs(preMatch[2])
364
411
  content = preMatch[3]
@@ -368,6 +415,9 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
368
415
  }
369
416
  }
370
417
  }
418
+ if (opt.useHighlightPre && hasPreTag && !hasHighlightPre) {
419
+ return content.endsWith('\n') ? content : content + '\n'
420
+ }
371
421
  orderTokenAttrs(token, opt)
372
422
 
373
423
  let preAttrs = preAttrsFromHighlight ? preAttrsFromHighlight.slice() : []
@@ -392,14 +442,15 @@ const getFenceHtml = (tokens, idx, md, opt, slf) => {
392
442
 
393
443
  const needLineNumber = opt.setLineNumber && startNumber >= 0
394
444
  const needEmphasis = opt.setEmphasizeLines && emphasizeLines.length > 0
395
- const needEndSpan = opt.setLineEndSpan > 0
396
- if (needLineNumber || needEmphasis || needEndSpan) {
445
+ const needEndSpan = opt.lineEndSpanThreshold > 0
446
+ const useHighlightPre = opt.useHighlightPre && hasHighlightPre
447
+ if (!useHighlightPre && (needLineNumber || needEmphasis || needEndSpan || needComment)) {
397
448
  const nlIndex = content.indexOf('\n')
398
449
  const br = nlIndex > 0 && content[nlIndex - 1] === '\r' ? '\r\n' : '\n'
399
- content = splitFenceBlockToLines(content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, opt.setLineEndSpan, opt.lineEndSpanClass, br)
450
+ content = splitFenceBlockToLines(content, emphasizeLines, needLineNumber, needEmphasis, needEndSpan, opt.lineEndSpanThreshold, opt.lineEndSpanClass, br, commentLines, commentLineClass)
400
451
  }
401
452
 
402
- const tag = opt._sampReg.test(lang) ? 'samp' : 'code'
453
+ const tag = isSamp ? 'samp' : 'code'
403
454
  return `<pre${preAttrsText}><${tag}${slf.renderAttrs(token)}>${content}</${tag}></pre>\n`
404
455
  }
405
456
 
@@ -409,13 +460,19 @@ const mditRendererFence = (md, option) => {
409
460
  setHighlight: true,
410
461
  setLineNumber: true,
411
462
  setEmphasizeLines: true,
412
- setLineEndSpan: 0,
463
+ lineEndSpanThreshold: 0,
413
464
  lineEndSpanClass: 'pre-lineend-spacer',
414
465
  setPreWrapStyle: true,
466
+ useHighlightPre: false,
415
467
  sampLang: 'shell,console',
416
468
  langPrefix: md.options.langPrefix || 'language-',
417
469
  }
418
- if (option) Object.assign(opt, option)
470
+ if (option) {
471
+ Object.assign(opt, option)
472
+ if (option.lineEndSpanThreshold == null && option.setLineEndSpan != null) {
473
+ opt.lineEndSpanThreshold = option.setLineEndSpan
474
+ }
475
+ }
419
476
 
420
477
  opt._sampReg = new RegExp('^(?:samp|' + opt.sampLang.split(',').join('|') + ')$')
421
478
  opt._langReg = new RegExp(opt.langPrefix + '([A-Za-z0-9-]+)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peaceroad/markdown-it-renderer-fence",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
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,7 +16,7 @@
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.15.1",
19
+ "@peaceroad/markdown-it-figure-with-p-caption": "^0.15.2",
20
20
  "highlight.js": "^11.11.1",
21
21
  "markdown-it": "^14.1.0",
22
22
  "markdown-it-attrs": "^4.3.1",