Skip to content

Commit

Permalink
feat: add highlight word
Browse files Browse the repository at this point in the history
  • Loading branch information
pengzhanbo committed Jan 31, 2024
1 parent d087249 commit 554167d
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 56 deletions.
5 changes: 3 additions & 2 deletions docs/2.preview/主题效果预览.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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
Expand Down
80 changes: 26 additions & 54 deletions plugins/plugin-shikiji/src/node/highlight.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
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,
): Promise<(str: string, lang: string, attrs: string) => string> {
const {
defaultHighlightLang: defaultLang = '',
codeTransformers: userTransformers = [],
whitespace = false,
} = options

const highlighter = await getHighlighter({
Expand All @@ -39,16 +45,17 @@ 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',
classActivePre: 'has-focused-lines',
}),
transformerNotationHighlight(),
transformerNotationErrorLevel(),
transformerNotationWordHighlight(),
{
name: 'vuepress:add-class',
pre(node) {
Expand All @@ -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)
Expand All @@ -94,8 +94,6 @@ export async function highlight(
const mustaches = new Map<string, string>()

const removeMustache = (s: string) => {
if (vPre)
return s
return s.replace(mustacheRE, (match) => {
let marker = mustaches.get(match)
if (!marker) {
Expand All @@ -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({
Expand All @@ -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,
Expand Down

0 comments on commit 554167d

Please sign in to comment.