refactor(build): replace the line highlighting logic with Shiki lineOptions (#1152)

pull/1155/head
Jinjiang 2 years ago committed by GitHub
parent 93fe8207e7
commit e8cb73e065
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -310,30 +310,21 @@
}
.vp-doc [class*='language-'] code {
display: block;
padding: 0 24px;
width: fit-content;
display: flex;
width: max-content;
min-width: 100%;
flex-direction: column;
line-height: var(--vp-code-line-height);
font-size: var(--vp-code-font-size);
color: var(--vp-code-block-color);
transition: color 0.5s;
}
.vp-doc .highlight-lines {
position: absolute;
top: 0;
bottom: 0;
left: 0;
padding-top: 16px;
width: 100%;
line-height: var(--vp-code-line-height);
font-family: var(--vp-font-family-mono);
font-size: var(--vp-code-font-size);
user-select: none;
overflow: hidden;
.vp-doc [class*='language-'] code .line {
padding: 0 24px;
}
.vp-doc .highlight-lines .highlighted {
.vp-doc [class*='language-'] code .highlighted {
background-color: var(--vp-code-line-highlight-color);
transition: background-color 0.5s;
}

@ -1,7 +1,40 @@
import { IThemeRegistration, getHighlighter } from 'shiki'
import { IThemeRegistration, getHighlighter, HtmlRendererOptions } from 'shiki'
import type { ThemeOptions } from '../markdown'
export async function highlight(theme: ThemeOptions = 'material-palenight') {
/**
* 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[] }]
*/
const attrsToLines = (attrs: string): HtmlRendererOptions['lineOptions'] => {
const result: number[] = []
if (!attrs.trim()) {
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 = 'material-palenight'
): Promise<(str: string, lang: string, attrs: string) => string> {
const hasSingleTheme = typeof theme === 'string' || 'name' in theme
const getThemeName = (themeValue: IThemeRegistration) =>
typeof themeValue === 'string' ? themeValue : themeValue.name
@ -12,22 +45,24 @@ export async function highlight(theme: ThemeOptions = 'material-palenight') {
const preRE = /^<pre.*?>/
const vueRE = /-vue$/
return (str: string, lang: string) => {
return (str: string, lang: string, attrs: string) => {
const vPre = vueRE.test(lang) ? '' : 'v-pre'
lang = lang.replace(vueRE, '').toLowerCase()
const lineOptions = attrsToLines(attrs)
if (hasSingleTheme) {
return highlighter
.codeToHtml(str, { lang, theme: getThemeName(theme) })
.codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme) })
.replace(preRE, `<pre ${vPre}>`)
}
const dark = highlighter
.codeToHtml(str, { lang, theme: getThemeName(theme.dark) })
.codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme.dark) })
.replace(preRE, `<pre ${vPre} class="vp-code-dark">`)
const light = highlighter
.codeToHtml(str, { lang, theme: getThemeName(theme.light) })
.codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme.light) })
.replace(preRE, `<pre ${vPre} class="vp-code-light">`)
return dark + light

@ -1,13 +1,15 @@
// 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'.
import MarkdownIt from 'markdown-it'
const RE = /{([\d,-]+)}/
const wrapperRE = /^<pre .*?><code>/
export const highlightLinePlugin = (md: MarkdownIt) => {
const fence = md.renderer.rules.fence!
md.renderer.rules.fence = (...args) => {
const [tokens, idx, options] = args
const [tokens, idx] = args
const token = tokens[idx]
// due to use of markdown-it-attrs, the {0} syntax would have been
@ -40,35 +42,7 @@ export const highlightLinePlugin = (md: MarkdownIt) => {
}
}
const lineNumbers = lines
.split(',')
.map((v) => v.split('-').map((v) => parseInt(v, 10)))
const code = options.highlight
? options.highlight(token.content, token.info, '')
: token.content
const rawCode = code.replace(wrapperRE, '')
const highlightLinesCode = rawCode
.split('\n')
.map((split, index) => {
const lineNumber = index + 1
const inRange = lineNumbers.some(([start, end]) => {
if (start && end) {
return lineNumber >= start && lineNumber <= end
}
return lineNumber === start
})
if (inRange) {
return `<div class="highlighted">&nbsp;</div>`
}
return '<br>'
})
.join('')
const highlightLinesWrapperCode = `<div class="highlight-lines">${highlightLinesCode}</div>`
return highlightLinesWrapperCode + code
token.info += ' ' + lines
return fence(...args)
}
}

Loading…
Cancel
Save