From f21b48589d58148b07fec63853f385e8a7bc44e8 Mon Sep 17 00:00:00 2001 From: Elecmonkey Date: Sun, 28 Sep 2025 01:09:05 +0800 Subject: [PATCH 1/5] feat(markdown): add languageLabel config and improve languageAlias display --- src/node/markdown/markdown.ts | 20 ++++++++++++++++++-- src/node/markdown/plugins/highlight.ts | 5 ++++- src/node/markdown/plugins/preWrapper.ts | 12 +++++++++++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index 7e6d09e8a0ac..9cdaff8503db 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -88,11 +88,24 @@ export interface MarkdownOptions extends Options { languages?: (LanguageInput | BuiltinLanguage)[] /** * Custom language aliases. + * Maps custom language names to existing languages for syntax highlighting. + * Language identifiers with underscores will automatically display with spaces. + * Alias lookup is case-insensitive. + * + * @example { 'my_lang': 'python' } + * + * ```My_Lang uses Python highlighting, displays "My Lang" * - * @example { 'my-lang': 'js' } * @see https://shiki.style/guide/load-lang#custom-language-aliases */ languageAlias?: Record + /** + * Custom language labels for display. + * Overrides the default language label shown in code blocks. + * + * @example { 'vue': 'Vue SFC' } + */ + languageLabel?: Record /** * Show line numbers in code blocks * @default false @@ -249,7 +262,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) diff --git a/src/node/markdown/plugins/highlight.ts b/src/node/markdown/plugins/highlight.ts index f1285ed7bdd2..4e62d3d19b3c 100644 --- a/src/node/markdown/plugins/highlight.ts +++ b/src/node/markdown/plugins/highlight.ts @@ -68,7 +68,10 @@ export async function highlight( ...(options.languages || []), ...Object.values(options.languageAlias || {}) ], - langAlias: options.languageAlias + langAlias: options.languageAlias ? + Object.fromEntries( + Object.entries(options.languageAlias).map(([key, value]) => [key.toLowerCase(), value]) + ) : undefined }) await options?.shikiSetup?.(highlighter) diff --git a/src/node/markdown/plugins/preWrapper.ts b/src/node/markdown/plugins/preWrapper.ts index e7d553b36368..d68043a17239 100644 --- a/src/node/markdown/plugins/preWrapper.ts +++ b/src/node/markdown/plugins/preWrapper.ts @@ -2,6 +2,7 @@ import type { MarkdownItAsync } from 'markdown-it-async' export interface Options { codeCopyButtonTitle: string + languageLabel?: Record } export function preWrapperPlugin(md: MarkdownItAsync, options: Options) { @@ -17,11 +18,12 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) { token.info = token.info.replace(/ active$/, '').replace(/ active /, ' ') const lang = extractLang(token.info) + const langLabel = getLangLabel(lang, options.languageLabel) return ( `
` + `` + - `${lang}` + + `${langLabel}` + fence(...args) + '
' ) @@ -46,3 +48,11 @@ function extractLang(info: string) { .replace(/^vue-html$/, 'template') .replace(/^ansi$/, '') } + +function getLangLabel(lang: string, languageLabel?: Record): string { + if (languageLabel && languageLabel[lang]) { + return languageLabel[lang] + } + + return lang.replace(/_/g, ' ') +} From 597e8e4468a9e2e0916cc460b5558b7064d3167b Mon Sep 17 00:00:00 2001 From: Elecmonkey Date: Sun, 28 Sep 2025 01:10:54 +0800 Subject: [PATCH 2/5] docs(en,zh): update for language label & language alias features --- docs/en/guide/markdown.md | 2 +- docs/zh/guide/markdown.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/guide/markdown.md b/docs/en/guide/markdown.md index c33424741a86..e1be3e6b366f 100644 --- a/docs/en/guide/markdown.md +++ b/docs/en/guide/markdown.md @@ -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 diff --git a/docs/zh/guide/markdown.md b/docs/zh/guide/markdown.md index cda368eea1e6..a401774ccfd4 100644 --- a/docs/zh/guide/markdown.md +++ b/docs/zh/guide/markdown.md @@ -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} From 60506d4a9b83cb06926fd21ac44b4e8d7bc12898 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Mon, 29 Sep 2025 01:46:54 +0530 Subject: [PATCH 3/5] escape fences in jsdoc, simplify, format --- src/node/markdown/markdown.ts | 25 ++++++++++++++++++------- src/node/markdown/plugins/highlight.ts | 12 ++++++++---- src/node/markdown/plugins/preWrapper.ts | 10 +--------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index 9cdaff8503db..a091639a2cab 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -87,14 +87,25 @@ export interface MarkdownOptions extends Options { */ languages?: (LanguageInput | BuiltinLanguage)[] /** - * Custom language aliases. - * Maps custom language names to existing languages for syntax highlighting. - * Language identifiers with underscores will automatically display with spaces. - * Alias lookup is case-insensitive. + * 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 { 'my_lang': 'python' } - * - * ```My_Lang uses Python highlighting, displays "My Lang" + * @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!") + * ``` + * ```` * * @see https://shiki.style/guide/load-lang#custom-language-aliases */ diff --git a/src/node/markdown/plugins/highlight.ts b/src/node/markdown/plugins/highlight.ts index 4e62d3d19b3c..780e37e879d9 100644 --- a/src/node/markdown/plugins/highlight.ts +++ b/src/node/markdown/plugins/highlight.ts @@ -68,10 +68,14 @@ export async function highlight( ...(options.languages || []), ...Object.values(options.languageAlias || {}) ], - langAlias: options.languageAlias ? - Object.fromEntries( - Object.entries(options.languageAlias).map(([key, value]) => [key.toLowerCase(), value]) - ) : undefined + langAlias: options.languageAlias + ? Object.fromEntries( + Object.entries(options.languageAlias).map(([key, value]) => [ + key.toLowerCase(), + value + ]) + ) + : undefined }) await options?.shikiSetup?.(highlighter) diff --git a/src/node/markdown/plugins/preWrapper.ts b/src/node/markdown/plugins/preWrapper.ts index d68043a17239..382665098cdd 100644 --- a/src/node/markdown/plugins/preWrapper.ts +++ b/src/node/markdown/plugins/preWrapper.ts @@ -18,7 +18,7 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) { token.info = token.info.replace(/ active$/, '').replace(/ active /, ' ') const lang = extractLang(token.info) - const langLabel = getLangLabel(lang, options.languageLabel) + const langLabel = options.languageLabel?.[lang] || lang.replace(/_/g, ' ') return ( `
` + @@ -48,11 +48,3 @@ function extractLang(info: string) { .replace(/^vue-html$/, 'template') .replace(/^ansi$/, '') } - -function getLangLabel(lang: string, languageLabel?: Record): string { - if (languageLabel && languageLabel[lang]) { - return languageLabel[lang] - } - - return lang.replace(/_/g, ' ') -} From d492d0211ce0b91847df53b305cdeffc849540d6 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Mon, 29 Sep 2025 01:54:34 +0530 Subject: [PATCH 4/5] make languageLabel case-insensitive too --- src/node/markdown/markdown.ts | 1 + src/node/markdown/plugins/highlight.ts | 14 ++++++-------- src/node/markdown/plugins/preWrapper.ts | 10 +++++++++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index a091639a2cab..be4b82500cb7 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -113,6 +113,7 @@ export interface MarkdownOptions extends Options { /** * Custom language labels for display. * Overrides the default language label shown in code blocks. + * Keys are case-insensitive. * * @example { 'vue': 'Vue SFC' } */ diff --git a/src/node/markdown/plugins/highlight.ts b/src/node/markdown/plugins/highlight.ts index 780e37e879d9..8a3dd9e0a2f0 100644 --- a/src/node/markdown/plugins/highlight.ts +++ b/src/node/markdown/plugins/highlight.ts @@ -68,14 +68,12 @@ export async function highlight( ...(options.languages || []), ...Object.values(options.languageAlias || {}) ], - langAlias: options.languageAlias - ? Object.fromEntries( - Object.entries(options.languageAlias).map(([key, value]) => [ - key.toLowerCase(), - value - ]) - ) - : undefined + langAlias: Object.fromEntries( + Object.entries(options.languageAlias || {}).map(([k, v]) => [ + k.toLowerCase(), + v + ]) + ) }) await options?.shikiSetup?.(highlighter) diff --git a/src/node/markdown/plugins/preWrapper.ts b/src/node/markdown/plugins/preWrapper.ts index 382665098cdd..c357c1d65646 100644 --- a/src/node/markdown/plugins/preWrapper.ts +++ b/src/node/markdown/plugins/preWrapper.ts @@ -6,6 +6,13 @@ export interface Options { } export function preWrapperPlugin(md: MarkdownItAsync, options: Options) { + const languageLabel = 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 @@ -18,7 +25,8 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) { token.info = token.info.replace(/ active$/, '').replace(/ active /, ' ') const lang = extractLang(token.info) - const langLabel = options.languageLabel?.[lang] || lang.replace(/_/g, ' ') + const langLabel = + languageLabel[lang.toLowerCase()] || lang.replace(/_/g, ' ') return ( `
` + From b1877e08ec6d0ed858949145e94d869fde719c5b Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Mon, 29 Sep 2025 02:00:42 +0530 Subject: [PATCH 5/5] minor tweaks --- src/node/markdown/plugins/highlight.ts | 17 +++++++---------- src/node/markdown/plugins/preWrapper.ts | 13 +++++-------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/node/markdown/plugins/highlight.ts b/src/node/markdown/plugins/highlight.ts index 8a3dd9e0a2f0..8f427823f0b8 100644 --- a/src/node/markdown/plugins/highlight.ts +++ b/src/node/markdown/plugins/highlight.ts @@ -59,21 +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: Object.fromEntries( - Object.entries(options.languageAlias || {}).map(([k, v]) => [ - k.toLowerCase(), - v - ]) - ) + langs: [...(options.languages || []), ...Object.values(langAlias)], + langAlias }) await options?.shikiSetup?.(highlighter) diff --git a/src/node/markdown/plugins/preWrapper.ts b/src/node/markdown/plugins/preWrapper.ts index c357c1d65646..b75fa6188d3f 100644 --- a/src/node/markdown/plugins/preWrapper.ts +++ b/src/node/markdown/plugins/preWrapper.ts @@ -6,11 +6,9 @@ export interface Options { } export function preWrapperPlugin(md: MarkdownItAsync, options: Options) { - const languageLabel = Object.fromEntries( - Object.entries(options.languageLabel || {}).map(([k, v]) => [ - k.toLowerCase(), - v - ]) + const langLabel = Object.fromEntries( + Object.entries(options.languageLabel || {}) // + .map(([k, v]) => [k.toLowerCase(), v]) ) const fence = md.renderer.rules.fence! @@ -25,13 +23,12 @@ export function preWrapperPlugin(md: MarkdownItAsync, options: Options) { token.info = token.info.replace(/ active$/, '').replace(/ active /, ' ') const lang = extractLang(token.info) - const langLabel = - languageLabel[lang.toLowerCase()] || lang.replace(/_/g, ' ') + const label = langLabel[lang.toLowerCase()] || lang.replace(/_/g, ' ') return ( `
` + `` + - `${langLabel}` + + `${label}` + fence(...args) + '
' )