Skip to content
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
2 changes: 1 addition & 1 deletion docs/en/guide/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export default {

A [list of valid languages](https://shiki.style/languages) is available on Shiki's repository.

You may also customize syntax highlight theme in app config. Please see [`markdown` options](../reference/site-config#markdown) for more details.
You may also customize syntax highlight theme, configure language aliases, and set custom language labels in app config. Please see [`markdown` options](../reference/site-config#markdown) for more details.

## Line Highlighting in Code Blocks

Expand Down
2 changes: 1 addition & 1 deletion docs/zh/guide/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ export default {

在 Shiki 的代码仓库中,可以找到[合法的编程语言列表](https://shiki.style/languages)。

还可以全局配置中自定义语法高亮主题。有关详细信息,参见 [`markdown` 选项](../reference/site-config#markdown)得到更多信息。
还可以在全局配置中自定义语法高亮主题、配置语言别名和自定义语言标签。有关详细信息,参见 [`markdown` 选项](../reference/site-config#markdown)得到更多信息。

## 在代码块中实现行高亮 {#line-highlighting-in-code-blocks}

Expand Down
34 changes: 31 additions & 3 deletions src/node/markdown/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,37 @@ export interface MarkdownOptions extends Options {
*/
languages?: (LanguageInput | BuiltinLanguage)[]
/**
* Custom language aliases.
* Custom language aliases for syntax highlighting.
* Maps custom language names to existing languages.
* Alias lookup is case-insensitive and underscores in language names are displayed as spaces.
*
* @example
*
* Maps `my_lang` to use Python syntax highlighting.
* ```js
* { 'my_lang': 'python' }
* ```
*
* Usage in markdown:
* ````md
* ```My_Lang
* # This will be highlighted as Python code
* # and will show "My Lang" as the language label
* print("Hello, World!")
* ```
* ````
*
* @example { 'my-lang': 'js' }
* @see https://shiki.style/guide/load-lang#custom-language-aliases
*/
languageAlias?: Record<string, string>
/**
* Custom language labels for display.
* Overrides the default language label shown in code blocks.
* Keys are case-insensitive.
*
* @example { 'vue': 'Vue SFC' }
*/
languageLabel?: Record<string, string>
/**
* Show line numbers in code blocks
* @default false
Expand Down Expand Up @@ -249,7 +274,10 @@ export async function createMarkdownRenderer(
// custom plugins
md.use(componentPlugin, { ...options.component })
.use(highlightLinePlugin)
.use(preWrapperPlugin, { codeCopyButtonTitle })
.use(preWrapperPlugin, {
codeCopyButtonTitle,
languageLabel: options.languageLabel
})
.use(snippetPlugin, srcDir)
.use(containerPlugin, options.container)
.use(imagePlugin, options.image)
Expand Down
12 changes: 7 additions & 5 deletions src/node/markdown/plugins/highlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,18 @@ export async function highlight(
codeTransformers: userTransformers = []
} = options

const langAlias = Object.fromEntries(
Object.entries(options.languageAlias || {}) //
.map(([k, v]) => [k.toLowerCase(), v])
)

const highlighter = await createHighlighter({
themes:
typeof theme === 'object' && 'light' in theme && 'dark' in theme
? [theme.light, theme.dark]
: [theme],
langs: [
...(options.languages || []),
...Object.values(options.languageAlias || {})
],
langAlias: options.languageAlias
langs: [...(options.languages || []), ...Object.values(langAlias)],
langAlias
})

await options?.shikiSetup?.(highlighter)
Expand Down
9 changes: 8 additions & 1 deletion src/node/markdown/plugins/preWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import type { MarkdownItAsync } from 'markdown-it-async'

export interface Options {
codeCopyButtonTitle: string
languageLabel?: Record<string, string>
}

export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
const langLabel = Object.fromEntries(
Object.entries(options.languageLabel || {}) //
.map(([k, v]) => [k.toLowerCase(), v])
)

const fence = md.renderer.rules.fence!
md.renderer.rules.fence = (...args) => {
const [tokens, idx] = args
Expand All @@ -17,11 +23,12 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) {
token.info = token.info.replace(/ active$/, '').replace(/ active /, ' ')

const lang = extractLang(token.info)
const label = langLabel[lang.toLowerCase()] || lang.replace(/_/g, ' ')

return (
`<div class="language-${lang}${active}">` +
`<button title="${options.codeCopyButtonTitle}" class="copy"></button>` +
`<span class="lang">${lang}</span>` +
`<span class="lang">${label}</span>` +
fence(...args) +
'</div>'
)
Expand Down