﻿---
title: Inline Code Highlighting with markdown-it & Shiki
date: 2025-11-21
tags:
  - Shiki
  - Markdown
  - Hexo
  - Blog
excerpt: 25/11/20和25/11/21期间开发了markdown-it-inline-code项目，用于高亮行内代码。本文包括效果预览、使用方法以及开发历程
updated: 2026-05-08 22:34:07
---

<script data-swup-reload-script type="module" src="/js/components/tab.js"></script>

> [!danger] 文章内容已过时
>
> 项目已换用 [Efterklang/hexo-renderer-markdown-exit](https://github.com/Efterklang/hexo-renderer-markdown-exit) + markdown-exit-shiki

先展示一下预览效果

<x-tabs>

<x-tab title="Markdown" active>

```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!"`
```

</x-tab>

<x-tab title="HTML">

- **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!"`

</x-tab>

</x-tabs>

> Tips: 如果使用Obsidian作为编辑器，可以下载 [obsidian-shiki-plugin](https://github.com/mProjectsCode/obsidian-shiki-plugin) 并开启 `inline-highlight` 特性

## 使用方法

### 下载 Markdown-it 渲染引擎

以**Hexo**为例，默认的渲染引擎（renderer）是 marked[^1]，其作用是将Markdown转换为HTML。例如：

-  将 `{md} **Bold**` 渲染为 `{html} <strong>Bold<strong/>`
- 将 `{md} *Italic*` 渲染为 `{html} <em>Italic<em/>`

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

```sh
bun uninstall hexo-renderer-marked --save
bun i hexo-renderer-markdown-it-plus --save
bun 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: markdown-it-inline-code
      enable: true
```

### 测试效果，设置自定义样式

运行 `{shell} hexo clean && hexo s` ，以 `{md} *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>
```

可以参考[[web_theme#代码高亮 —— Shiki|我的这篇文章]]为其适配主题切换，这里提供一个基于属性选择器进行切换的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版本，代码质量有待提高，但是

![罗老师](https://assets.vluv.space/又不是不能用.avif)

### 同步使用 Shiki

起初使用Shiki的 `codeToHtml` 接口，是个异步接口，返回一个Promise对象。由于markdown-it的 `render.rules` 只能是同步的，于是参考了[同步使用 | Shiki 中文文档](https://shiki.zhcndoc.com/guide/sync-usage#%E5%90%8C%E6%AD%A5%E4%BD%BF%E7%94%A8)，使用 `{js} 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/src
import { allLangs } from './all-langs'
// theme
import 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() { // [!code warning]
    const g = globalThis as any // [!code warning]
    if (g[SHIKI_KEY]) return g[SHIKI_KEY] // [!code warning]
 // [!code warning]
    // Handle potential default export wrapping when bundled/externalized // [!code warning]
    const themeList = themes.map(t => (t as any).default || t) // [!code warning]
 // [!code warning]
    const shiki = createHighlighterCoreSync({ // [!code warning]
        langs: allLangs, // [!code warning]
        themes: themeList, // [!code warning]
        engine: createJavaScriptRegexEngine() // [!code warning]
    }) // [!code warning]
    g[SHIKI_KEY] = shiki // [!code warning]
    return shiki // [!code warning]
} // [!code warning]
```

### 自定义渲染规则

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

1. 使用正则表达式匹配代码内容，提取出语言和代码两部分
2. 调用 `shiki.codeToHtml` 方法，同步完成代码高亮

```ts
function inlineCodeHighlightPlugin( md: MarkdownIt, _options: null ) {
    // 获取shiki singleton实例
    const shiki = getShiki() // [!code highlight]

    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], { // [!code highlight]
                lang: match[1], // [!code highlight]
                themes: themeMap, // [!code highlight]
                structure: 'inline' // [!code highlight]
            }) // [!code highlight]

            return '<code' + self.renderAttrs(token) + '>' + highlighted + '</code>'
        } catch (e) {
            console.error('Highlighting failed', e)
            return defaultRender(tokens, idx, options, env, self)
        }
    }
}

export default inlineCodeHighlightPlugin
```

[^1]: A markdown parser and compiler. Built for speed. [markedjs/marked: A markdown parser and compiler. Built for speed.](https://github.com/markedjs/marked)
[^2]: Markdown-it is a markdown parser, done right. A faster and CommonMark compliant alternative for Hexo.  [CHENXCHEN/hexo-renderer-markdown-it-plus.](https://github.com/CHENXCHEN/hexo-renderer-markdown-it-plus) 插件很久没维护了，不过插件功能简单，本身也无太多开发需求。后续有需要则可能会考虑fork二开，或者找下其他替代
