Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace the line highlighting logic with Shiki lineOptions #1152

Merged
merged 2 commits into from
Aug 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions src/client/theme-default/styles/components/vp-doc.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
47 changes: 41 additions & 6 deletions src/node/markdown/plugins/highlight.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
38 changes: 6 additions & 32 deletions src/node/markdown/plugins/highlightLines.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
}
}