Skip to content

Commit

Permalink
perf(plugin-md-power): optimize collapsed-lines (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
pengzhanbo authored Sep 14, 2024
1 parent be47c9a commit 0f1ffc7
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 25 deletions.
61 changes: 61 additions & 0 deletions plugins/plugin-shikiji/src/node/markdown/collapsedLinesPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { Markdown } from 'vuepress/markdown'
import { resolveCollapsedLines } from '../utils/index.js'

export interface MarkdownItCollapsedLinesOptions {
/**
* Whether to collapse code blocks when they exceed a certain number of lines,
*
* - If `number`, collapse starts from line `number`.
* - If `true`, collapse starts from line 15 by default.
* - If `false`, disable collapse.
* @default false
*/
collapsedLines?: boolean | number
}

export function collapsedLinesPlugin(md: Markdown, {
collapsedLines: collapsedLinesOptions = false,
}: MarkdownItCollapsedLinesOptions = {}): void {
const rawFence = md.renderer.rules.fence!

md.renderer.rules.fence = (...args) => {
const [tokens, index] = args
const token = tokens[index]
// get token info
const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
const code = rawFence(...args)

// resolve collapsed-lines mark from token info
const collapsedLinesInfo
= resolveCollapsedLines(info) ?? collapsedLinesOptions

if (collapsedLinesInfo === false) {
return code
}

const lines
= code.slice(code.indexOf('<code>'), code.indexOf('</code>')).split('\n').length

const startLines
= typeof collapsedLinesInfo === 'number' ? collapsedLinesInfo : 15

if (lines < startLines) {
return code
}

const collapsedLinesCode = `<div class="collapsed-lines"></div>`
const styles = `--vp-collapsed-lines:${startLines};`

const finalCode = code
.replace(/<\/div>$/, `${collapsedLinesCode}</div>`)
.replace(/"(language-[^"]*)"/, '"$1 has-collapsed collapsed"')
.replace(/^<div[^>]*>/, (match) => {
if (!match.includes('style=')) {
return `${match.slice(0, -1)} style="${styles}">`
}
return match.replace(/(style=")/, `$1${styles}`)
})

return finalCode
}
}
1 change: 1 addition & 0 deletions plugins/plugin-shikiji/src/node/markdown/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './collapsedLinesPlugin.js'
export * from './highlightLinesPlugin.js'
export * from './lineNumberPlugin.js'
export * from './preWrapperPlugin.js'
10 changes: 2 additions & 8 deletions plugins/plugin-shikiji/src/node/markdown/preWrapperPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// markdown-it plugin for generating line numbers.
// v-pre block logic is in `../highlight.ts`
import type { Markdown } from 'vuepress/markdown'
import { resolveAttr, resolveCollapsedLines, resolveLanguage } from '../utils/index.js'
import { resolveAttr, resolveLanguage } from '../utils/index.js'
import type { PreWrapperOptions } from '../types.js'

export function preWrapperPlugin(
md: Markdown,
{ preWrapper = true, collapsedLines = false }: PreWrapperOptions = {},
{ preWrapper = true }: PreWrapperOptions = {},
): void {
const rawFence = md.renderer.rules.fence!

Expand All @@ -33,12 +33,6 @@ export function preWrapperPlugin(
`data-ext="${lang}"`,
`data-title="${title}"`,
]
const collapsed = resolveCollapsedLines(info, collapsedLines)
if (collapsed) {
classes.push('has-collapsed', 'collapsed')
attrs.push(`style="--vp-collapsed-lines:${collapsed}"`)
result += `<div class="collapsed-lines"></div>`
}

return `<div class="${classes.join(' ')}" ${attrs.join(' ')}>${result}</div>`
}
Expand Down
9 changes: 4 additions & 5 deletions plugins/plugin-shikiji/src/node/shikiPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Plugin } from 'vuepress/core'
import { copyCodeButtonPlugin } from './copy-code-button/index.js'
import { highlight } from './highlight/index.js'
import {
collapsedLinesPlugin,
highlightLinesPlugin,
lineNumberPlugin,
preWrapperPlugin,
Expand Down Expand Up @@ -53,14 +54,12 @@ export function shikiPlugin({
md.options.highlight = await highlight(theme, options)

md.use(highlightLinesPlugin)
md.use(preWrapperPlugin, {
preWrapper,
collapsedLines,
})
md.use(preWrapperPlugin, { preWrapper })

if (preWrapper) {
copyCodeButtonPlugin(md, app, copyCode)
md.use<LineNumberOptions>(lineNumberPlugin, { lineNumbers })
md.use(lineNumberPlugin, { lineNumbers })
md.use(collapsedLinesPlugin, { collapsedLines })
}
},

Expand Down
30 changes: 18 additions & 12 deletions plugins/plugin-shikiji/src/node/utils/collapsedLines.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
export const COLLAPSED_LINES_REGEXP = /:collapsed-lines(?:=(\d+))?\b/
export const NO_COLLAPSED_LINES_REGEXP = /:no-collapsed-lines\b/
const COLLAPSED_LINES_REGEXP = /:collapsed-lines\b/
const COLLAPSED_LINES_START_REGEXP = /:collapsed-lines=(\d+)\b/
const NO_COLLAPSED_LINES_REGEXP = /:no-collapsed-lines\b/

const DEFAULT_LINES = 15
/**
* Resolve the `:collapsed-lines` `:collapsed-lines=num` / `:no-collapsed-lines` mark from token info
*/
export function resolveCollapsedLines(info: string): boolean | number | null {
const lines = COLLAPSED_LINES_START_REGEXP.exec(info)?.[1]

export function resolveCollapsedLines(info: string, defaultLines: boolean | number): number | false {
if (NO_COLLAPSED_LINES_REGEXP.test(info))
return false

const lines = defaultLines === true ? DEFAULT_LINES : defaultLines
if (lines) {
return Number(lines)
}

const match = info.match(COLLAPSED_LINES_REGEXP)
if (COLLAPSED_LINES_REGEXP.test(info)) {
return true
}

if (match) {
return Number(match[1]) || lines || DEFAULT_LINES
if (NO_COLLAPSED_LINES_REGEXP.test(info)) {
return false
}
return lines ?? false

return null
}

0 comments on commit 0f1ffc7

Please sign in to comment.