From bbacb31c0aeca2611c7818eda366864089231581 Mon Sep 17 00:00:00 2001 From: Fro-Q Date: Thu, 24 Apr 2025 15:52:30 +0800 Subject: [PATCH 1/3] refactor: use shiki `transformerMetaHighlight` to do meta highlight --- src/node/markdown/plugins/highlight.ts | 38 ++------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/src/node/markdown/plugins/highlight.ts b/src/node/markdown/plugins/highlight.ts index f1285ed7..621b3b96 100644 --- a/src/node/markdown/plugins/highlight.ts +++ b/src/node/markdown/plugins/highlight.ts @@ -1,10 +1,9 @@ import { - transformerCompactLineOptions, + transformerMetaHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, - type TransformerCompactLineOption } from '@shikijs/transformers' import { customAlphabet } from 'nanoid' import c from 'picocolors' @@ -15,38 +14,6 @@ import type { MarkdownOptions, ThemeOptions } from '../markdown' const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) -/** - * 2 steps: - * - * 1. convert attrs into line numbers: - * {4,7-13,16,23-27,40} -> [4,7,8,9,10,11,12,13,16,23,24,25,26,27,40] - * 2. convert line numbers into line options: - * [{ line: number, classes: string[] }] - */ -function attrsToLines(attrs: string): TransformerCompactLineOption[] { - attrs = attrs.replace(/^(?:\[.*?\])?.*?([\d,-]+).*/, '$1').trim() - const result: number[] = [] - if (!attrs) { - return [] - } - attrs - .split(',') - .map((v) => v.split('-').map((v) => parseInt(v, 10))) - .forEach(([start, end]) => { - if (start && end) { - result.push( - ...Array.from({ length: end - start + 1 }, (_, i) => start + i) - ) - } else { - result.push(start) - } - }) - return result.map((v) => ({ - line: v, - classes: ['highlighted'] - })) -} - export async function highlight( theme: ThemeOptions, options: MarkdownOptions, @@ -74,6 +41,7 @@ export async function highlight( await options?.shikiSetup?.(highlighter) const transformers: ShikiTransformer[] = [ + transformerMetaHighlight(), transformerNotationDiff(), transformerNotationFocus({ classActiveLine: 'has-focus', @@ -121,7 +89,6 @@ export async function highlight( lang = defaultLang } - const lineOptions = attrsToLines(attrs) const mustaches = new Map() const removeMustache = (s: string) => { @@ -152,7 +119,6 @@ export async function highlight( lang, transformers: [ ...transformers, - transformerCompactLineOptions(lineOptions), { name: 'vitepress:v-pre', pre(node) { From 8ac2cb92ddec2641518651cd797b2f1771124b25 Mon Sep 17 00:00:00 2001 From: Fro-Q Date: Thu, 24 Apr 2025 16:05:04 +0800 Subject: [PATCH 2/3] refactor: update highlightLines.ts to support multi-key meta notation like `add={1} remove={2}` --- src/node/markdown/plugins/highlightLines.ts | 39 ++++++++------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/src/node/markdown/plugins/highlightLines.ts b/src/node/markdown/plugins/highlightLines.ts index a66882ec..aadc9b42 100644 --- a/src/node/markdown/plugins/highlightLines.ts +++ b/src/node/markdown/plugins/highlightLines.ts @@ -1,48 +1,37 @@ // Modified from https://github.com/egoist/markdown-it-highlight-lines -// Now this plugin is only used to normalize line attrs. // The else part of line highlights logic is in './highlight.ts'. +// Restore the `{0}` syntax recognized and processed by the markdown-it-attrs plugin import type { MarkdownItAsync } from 'markdown-it-async' -const RE = /{([\d,-]+)}/ - export const highlightLinePlugin = (md: MarkdownItAsync) => { const fence = md.renderer.rules.fence! md.renderer.rules.fence = (...args) => { const [tokens, idx] = args const token = tokens[idx] - // due to use of markdown-it-attrs, the {0} syntax would have been + // due to use of markdown-it-attrs, the last `{0}` syntax would have been // converted to attrs on the token + // e.g.: `js add={1} remove={2}` => `js add={1} remove=` + // `{2}` is stored in token.attrs const attr = token.attrs && token.attrs[0] - let lines = null + let removedLines = null if (!attr) { - // markdown-it-attrs maybe disabled - const rawInfo = token.info - - if (!rawInfo || !RE.test(rawInfo)) { - return fence(...args) - } - - const langName = rawInfo.replace(RE, '').trim() - - // ensure the next plugin get the correct lang - token.info = langName - - lines = RE.exec(rawInfo)![1] + return fence(...args) } - if (!lines) { - lines = attr![0] - - if (!lines || !/[\d,-]+/.test(lines)) { - return fence(...args) - } + removedLines = attr[0] + if (!removedLines || !/[\d,-]+/.test(removedLines)) { + return fence(...args) } - token.info += ' ' + lines + // except for case like `js{1}`, no trailing space + token.info += token.info.endsWith('=') ? '' : ' ' + // add the line numbers back to the token + token.info += "{" + removedLines + "}" + return fence(...args) } } From 488881fda7e65380b4c414e30869cfa2c14f909e Mon Sep 17 00:00:00 2001 From: Fro-Q Date: Thu, 24 Apr 2025 16:46:58 +0800 Subject: [PATCH 3/3] fix: bug that `{1}` in `js{1} add={2}` is not dealed to result in langName being `js{1}` --- src/node/markdown/plugins/highlightLines.ts | 41 ++++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/node/markdown/plugins/highlightLines.ts b/src/node/markdown/plugins/highlightLines.ts index aadc9b42..34c6ce8b 100644 --- a/src/node/markdown/plugins/highlightLines.ts +++ b/src/node/markdown/plugins/highlightLines.ts @@ -1,37 +1,50 @@ // Modified from https://github.com/egoist/markdown-it-highlight-lines +// Now this plugin is only used to normalize line attrs. // The else part of line highlights logic is in './highlight.ts'. -// Restore the `{0}` syntax recognized and processed by the markdown-it-attrs plugin import type { MarkdownItAsync } from 'markdown-it-async' +const RE = /{([\d,-]+)}/ + export const highlightLinePlugin = (md: MarkdownItAsync) => { const fence = md.renderer.rules.fence! md.renderer.rules.fence = (...args) => { const [tokens, idx] = args const token = tokens[idx] - // due to use of markdown-it-attrs, the last `{0}` syntax would have been + // due to use of markdown-it-attrs, the {0} syntax would have been // converted to attrs on the token - // e.g.: `js add={1} remove={2}` => `js add={1} remove=` - // `{2}` is stored in token.attrs const attr = token.attrs && token.attrs[0] - let removedLines = null + let lines = null if (!attr) { - return fence(...args) - } + // markdown-it-attrs maybe disabled + const rawInfo = token.info + + if (!rawInfo || !RE.test(rawInfo)) { + return fence(...args) + } + + const langName = rawInfo.replace(RE, '').trim() - removedLines = attr[0] - if (!removedLines || !/[\d,-]+/.test(removedLines)) { - return fence(...args) + // ensure the next plugin get the correct lang + token.info = langName + + lines = RE.exec(rawInfo)![1] } - // except for case like `js{1}`, no trailing space - token.info += token.info.endsWith('=') ? '' : ' ' - // add the line numbers back to the token - token.info += "{" + removedLines + "}" + if (!lines) { + lines = attr![0] + + if (!lines || !/[\d,-]+/.test(lines)) { + return fence(...args) + } + } + token.info += '{' + lines + '}' + // ensure there is a space between the lang and the line numbers + token.info = token.info.replace(/(?