From 554167d123a6d66cb0d496ad338c7f40e5115be8 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Wed, 31 Jan 2024 08:23:01 +0800 Subject: [PATCH] feat: add highlight word --- ...10\346\236\234\351\242\204\350\247\210.md" | 5 +- plugins/plugin-shikiji/src/node/highlight.ts | 80 ++++++------------- 2 files changed, 29 insertions(+), 56 deletions(-) diff --git "a/docs/2.preview/\344\270\273\351\242\230\346\225\210\346\236\234\351\242\204\350\247\210.md" "b/docs/2.preview/\344\270\273\351\242\230\346\225\210\346\236\234\351\242\204\350\247\210.md" index d505d8c7a..0e4601c1f 100644 --- "a/docs/2.preview/\344\270\273\351\242\230\346\225\210\346\236\234\351\242\204\350\247\210.md" +++ "b/docs/2.preview/\344\270\273\351\242\230\346\225\210\346\236\234\351\242\204\350\247\210.md" @@ -128,11 +128,12 @@ H~2~O **代码** -```js +```js whitespace const a = 1 const b = 2 const c = a + b +// [!code word:obj] const obj = { toLong: { deep: { @@ -164,7 +165,7 @@ app.listen(3000) ``` ```ts twoslash -import { getHighlighterCore } from 'shikiji/core' +import { getHighlighterCore } from 'shiki/core' const highlighter = await getHighlighterCore({}) // @log: Custom log message diff --git a/plugins/plugin-shikiji/src/node/highlight.ts b/plugins/plugin-shikiji/src/node/highlight.ts index 098321f30..528a040ff 100644 --- a/plugins/plugin-shikiji/src/node/highlight.ts +++ b/plugins/plugin-shikiji/src/node/highlight.ts @@ -1,26 +1,31 @@ import { logger } from 'vuepress/utils' import { customAlphabet } from 'nanoid' import c from 'picocolors' -import type { ShikijiTransformer } from 'shikiji' +import type { ShikiTransformer } from 'shiki' import { addClassToHast, bundledLanguages, getHighlighter, - isPlaintext as isPlainLang, + isPlainLang, isSpecialLang, -} from 'shikiji' +} from 'shiki' import { transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, -} from 'shikiji-transformers' -import { rendererRich, transformerTwoslash } from 'shikiji-twoslash' + transformerNotationWordHighlight, + transformerRenderWhitespace, +} from '@shikijs/transformers' +import { rendererRich, transformerTwoslash } from '@shikijs/twoslash' import type { HighlighterOptions, ThemeOptions } from './types.js' import { resolveAttrs } from './resolveAttrs.js' const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10) +const RE_ESCAPE = /\[\\\!code/g +const mustacheRE = /\{\{.*?\}\}/g + export async function highlight( theme: ThemeOptions, options: HighlighterOptions, @@ -28,6 +33,7 @@ export async function highlight( const { defaultHighlightLang: defaultLang = '', codeTransformers: userTransformers = [], + whitespace = false, } = options const highlighter = await getHighlighter({ @@ -39,9 +45,9 @@ export async function highlight( langAlias: options.languageAlias, }) - await options?.shikijiSetup?.(highlighter) + await options?.shikiSetup?.(highlighter) - const transformers: ShikijiTransformer[] = [ + const transformers: ShikiTransformer[] = [ transformerNotationDiff(), transformerNotationFocus({ classActiveLine: 'has-focus', @@ -49,6 +55,7 @@ export async function highlight( }), transformerNotationHighlight(), transformerNotationErrorLevel(), + transformerNotationWordHighlight(), { name: 'vuepress:add-class', pre(node) { @@ -62,21 +69,14 @@ export async function highlight( delete node.properties.style }, }, + { + name: 'vuepress-shikiji:remove-escape', + postprocess: code => code.replace(RE_ESCAPE, '[!code'), + }, ] - const vueRE = /-vue$/ - const lineNoStartRE = /=(\d*)/ - const lineNoRE = /:(no-)?line-numbers(=\d*)?$/ - const mustacheRE = /\{\{.*?\}\}/g - return (str: string, lang: string, attrs: string) => { - const vPre = vueRE.test(lang) ? '' : 'v-pre' - lang - = lang - .replace(lineNoStartRE, '') - .replace(lineNoRE, '') - .replace(vueRE, '') - .toLowerCase() || defaultLang + lang = lang || defaultLang if (lang) { const langLoaded = highlighter.getLoadedLanguages().includes(lang as any) @@ -94,8 +94,6 @@ export async function highlight( const mustaches = new Map() const removeMustache = (s: string) => { - if (vPre) - return s return s.replace(mustacheRE, (match) => { let marker = mustaches.get(match) if (!marker) { @@ -116,39 +114,7 @@ export async function highlight( str = removeMustache(str).trimEnd() - const inlineTransformers: ShikijiTransformer[] = [ - { - name: 'vuepress-shikiji:empty-line', - pre(hast) { - hast.children.forEach((code) => { - if (code.type === 'element' && code.tagName === 'code') { - code.children.forEach((span) => { - if ( - span.type === 'element' - && span.tagName === 'span' - && Array.isArray(span.properties.class) - && span.properties.class.includes('line') - && span.children.length === 0 - ) { - span.children.push({ - type: 'element', - tagName: 'wbr', - properties: {}, - children: [], - }) - } - }) - } - }) - }, - }, - { - name: 'vuepress-shikiji:remove-escape', - postprocess(code) { - return code.replace(/\[\\\!code/g, '[!code') - }, - }, - ] + const inlineTransformers: ShikiTransformer[] = [] if (attributes.twoslash) { inlineTransformers.push(transformerTwoslash({ @@ -158,6 +124,12 @@ export async function highlight( })) } + if ( + (whitespace && attributes.whitespace !== false) + || (!whitespace && attributes.whitespace) + ) + inlineTransformers.push(transformerRenderWhitespace({ position: 'boundary' })) + try { const highlighted = highlighter.codeToHtml(str, { lang,