Choose Theme

Shortcuts: ⌘ ⇧ P
↑↓ Navigate • Enter Confirm • Esc Close
🖥️System
🌻Latte
🦭Nord
🌺Macchiato
🌿Mocha
🏙Tokyo Night

使用Shiki高亮行内代码

于25/11/20和25/11/21期间开发了 markdown-it-inline-code 项目,用于高亮行内代码。本文包括效果预览使用方法,以及部分开发历程

先展示一下预览效果

MD
- **Plain**: `printf("Hello, World")`- **Python**: `{python} print("Hello, World")`- **JavaScript**: `{javascript} console.log("Hello, World")`- **HTML**: `{html} <h1>Hello, World!</h1>`- **Rust** `{rust} fn main() { println!("Hello, World!"); }`- **Shell**: `{shell} echo "Hello, World!"`
Tip

写作过程中,可能觉得 {lang} 前缀看起来比较多余,如果使用Obsidian作为编辑器,可以下载 obsidian-shiki-plugin 并开启 inline-highlight 特性,效果如下

使用方法

下线 markdown-it 渲染引擎

Hexo为例,默认的渲染引擎(renderer)是 marked[1],其作用是将Markdown转换为HTML。例如:

  • **Bold** 渲染为 <strong>Bold<strong/>
  • *Italic* 渲染为 <em>Italic<em/>

本插件基于 markdown-it 渲染引擎开发,因此需要先将渲染引擎切换为 markdown-it。这里个人使用 hexo-renderer-markdown-it-plus插件[2],在Hexo博客的根目录运行如下命令:

SHELL
bun uninstall hexo-renderer-marked --savebun i hexo-renderer-markdown-it-plus --savebun i markdown-it-inline-code@0.2.0

修改 Hexo配置:

YML
_config.yml
markdown_it_plus:  highlight: false  html: true  xhtmlOut: true  breaks: true  langPrefix: null  linkify: true  typographer: null  quotes: “”‘’  pre_class: highlight  plugins:    - plugin:      name: markdonwn-it-inline-code      enable: true

测试效果,设置自定义样式

运行 hexo clean && hexo s ,以 *Italic* 为例,预期渲染结果如下

HTML
<code>  <span    style="      color: #d20f39;      --shiki-light-font-style: italic;      --shiki-dark: #f38ba8;      --shiki-dark-font-style: italic;      --shiki-tokyo: #c0caf5;      --shiki-tokyo-font-style: italic;    "  >    Italic  </span></code>

可以参考我的这篇文章为其适配主题切换,这里提供一个基于属性选择器进行切换的CSS Demo

CSS
code span {  font-style: var(--shiki-light-font-style);}:where([data-theme="tokyo_night"]) {  code span {    font-style: var(--shiki-tokyo-font-style);    color: var(--shiki-tokyo);  }}:where([data-theme="mocha"], [data-theme="macchiato"]) {  code span {    font-style: var(--shiki-dark-font-style);    color: var(--shiki-dark);  }}

开发历程

起初看了下markdown-it的插件开发文档,觉得应该很简单,于是便动手开发了。打算睡前完成,结果折腾到4点后放弃了,一是因为笨人是ts, js低手,二是因为睡前不宜写代码

今天又整理了一下代码,Vibe Coding出了一个Beta版本,代码质量有待提高,但是

罗老师

同步使用 Shiki

起初使用Shiki的 codeToHtml 接口,是个异步接口,返回一个Promise对象。由于markdown-it的 render.rules 只能是同步的,于是参考了同步使用 | Shiki 中文文档,使用 createJavaScriptRegexEngine({lang, themes, engine}) 同步创建高亮器实例,有一点不方便的就是不能由用户传入配置,动态加载语言和主题了,不过用于自娱自乐足矣👍

TS
index.ts
import { createHighlighterCoreSync } from 'shiki/core'import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'// allLangs: list, shikijs所支持的语言//  all-langs.ts in Efterklang/markdown-it-inline-code/srcimport { allLangs } from './all-langs'// themeimport latte from '@shikijs/themes/catppuccin-latte'import mocha from '@shikijs/themes/catppuccin-mocha'import tokyo_night from '@shikijs/themes/tokyo-night'const themes = [latte, mocha, tokyo_night]const SHIKI_KEY = Symbol.for('mdit-inline-code-shiki')// shiki全局singleton实例function getShiki() {    const g = globalThis as any    if (g[SHIKI_KEY]) return g[SHIKI_KEY]     // Handle potential default export wrapping when bundled/externalized    const themeList = themes.map(t => (t as any).default || t)     const shiki = createHighlighterCoreSync({        langs: allLangs,        themes: themeList,        engine: createJavaScriptRegexEngine()     })     g[SHIKI_KEY] = shiki    return shiki}

自定义渲染规则

可以修改 MarkdownIt 实例的 renderer.rules.code_inline 方法,来自定义行内代码的渲染规则,这里的思路是:

  1. 使用正则表达式匹配代码内容,提取出语言和代码两部分
  2. 调用 shiki.codeToHtml 方法,同步完成代码高亮
TS
function inlineCodeHighlightPlugin( md: MarkdownIt, _options: null ) {    // 获取shiki singleton实例    const shiki = getShiki()     const defaultRender =        md.renderer.rules.code_inline ||        function (tokens, idx, _options, _env, self) {            const token = tokens[idx]            if (!token) return ''            return (                '<code' +                self.renderAttrs(token) +                '>' +                md.utils.escapeHtml(token.content) +                '</code>'            )        }    const themeMap = {        light: 'catppuccin-latte',        dark: 'catppuccin-mocha',        tokyo: 'tokyo-night'    }    md.renderer.rules.code_inline = function (tokens, idx, options, env, self) {        const token = tokens[idx]        if (!token) return ''        const content = token.content.trim()        // 于`{lang} code`中捕获lang和code        const match = content.match(/^\{(\w+)\}\s+(.+)$/)        if (match === null) {            return defaultRender(tokens, idx, options, env, self)        }        try {            const highlighted = shiki.codeToHtml(match[2], {                lang: match[1],                themes: themeMap,                structure: 'inline'            })             return '<code' + self.renderAttrs(token) + '>' + highlighted + '</code>'        } catch (e) {            console.error('Highlighting failed', e)            return defaultRender(tokens, idx, options, env, self)        }    }}export default inlineCodeHighlightPlugin

Markdown-it 异步渲染

发现Antfu的 markdown-it-async 项目和Hexo Renderer文档[3],重写了 hexo-renderer-markdown-it-plus 的渲染器,改为异步渲染。伪代码如下:

JS
// file: lib/renderer.jsconst { MarkdownItAsync } = require('markdown-it-async');module.exports = async function (data, options) {    let md = new MarkdownItAsync(parseConfig);    // init plugin and options    md.use(plugin, options);    return await md.renderAsync(data.text);}// file: index.jsvar mdit_renderer = require('./lib/renderer');// Promise-based async renderer https://hexo.io/api/renderingfunction asyncRenderer(data, options) {    return mdit_renderer.call(this, data, options);}hexo.extend.renderer.register('md', 'html', asyncRenderer);

发现用了之后,hexo.extend.tag.register() 注册的标签内的markdown无法被正确渲染,搞不懂,弃坑了


  1. A markdown parser and compiler. Built for speed. markedjs/marked: A markdown parser and compiler. Built for speed. ↩︎

  2. Markdown-it is a markdown parser, done right. A faster and CommonMark compliant alternative for Hexo. CHENXCHEN/hexo-renderer-markdown-it-plus. 插件很久没维护了,不过插件功能简单,本身也无太多开发需求。后续有需要则可能会考虑fork二开,或者找下其他替代 ↩︎

  3. There are two methods for rendering files or strings in Hexo: the asynchronous hexo.render.render method and the synchronous hexo.render.renderSync method. Rendering | Hexo ↩︎

使用Shiki高亮行内代码

https://vluv.space/inline_code_highlight/

作者

GnixAij

发布于

2025-11-21

更新于

2025-11-21

许可协议

评论