Chapter 05 / 10

代码块与语法高亮

内联代码、围栏代码块、语法高亮原理、diff 格式与代码块的各种用法

内联代码(Inline Code)

用单个反引号 ` 包裹的内容显示为等宽字体(Monospace),对应 HTML 的 <code> 标签。它的内部不会解析 Markdown 语法。

源码
运行 `npm install` 安装依赖。
变量 `userName` 不要写成 `user_name`。
配置文件在 `~/.config/app.json`。
函数签名:`getUserById(id: string)`
渲染效果

运行 npm install 安装依赖。

变量 userName 不要写成 user_name

配置文件在 ~/.config/app.json

函数签名:getUserById(id: string)

内联代码中包含反引号

如果内联代码本身需要包含反引号字符,用多个反引号作为包裹符:

源码
# 双反引号包裹,内部可含单反引号
`` 使用 ` 在 shell 中执行命令 ``
``var x = `template ${str}` ``

# 三反引号包裹,内部可含双反引号
``` 包含 `` 双反引号 `` 的内容 ```
渲染效果

使用 ` 在 shell 中执行命令

var x = `template ${str}`

INFO(反引号包裹规则)内联代码的包裹反引号数量可以是任意的,只要起始和结尾相同即可。内部的空格处理规则:如果代码两端各有一个空格,这两个空格会被删除(例如 ` code ` 等价于 `code`),这是为了方便写 `` ` `` 单个反引号(在两端各加一个空格)。

围栏代码块(Fenced Code Blocks)

多行代码使用"围栏"(Fence)包裹:三个及以上反引号(```)或三个及以上波浪线(~~~)。

基本语法

源码(反引号风格)
```python
def greet(name: str) -> str:
    """返回问候语"""
    return f"Hello, {name}!"

print(greet("World"))  # Hello, World!
```
渲染效果
def greet(name: str) -> str:
    """返回问候语"""
    return f"Hello, {name}!"

print(greet("World"))  # Hello, World!

波浪线风格(~~~)

波浪线和反引号在功能上完全等价,区别是:

~~~yaml
# docker-compose.yml
services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
~~~

围栏起始行可附加信息(Info String)

开始围栏行(```)后面除了语言标识符,还可以加其他信息,部分工具会利用这些信息:

# 某些平台支持显示文件名
```python title="hello.py"
print("Hello")
```

# 某些平台支持高亮特定行
```javascript {2,4-6}
function sum(a, b) {
  return a + b;  // 第2行高亮
}
const result = sum(
  1,              // 第4-6行高亮
  2
);
```

# Docusaurus 支持显示行号
```python showLineNumbers
def hello():
    print("Hello, World!")
```
INFO(Info String 支持情况)只有语言标识符是 CommonMark 规范的一部分,其他扩展信息(title、行号、行高亮)是特定平台的扩展功能。GitHub 只识别语言标识符,会忽略其他信息。Docusaurus、VitePress、Notion 等平台各有自己的扩展语法。

语法高亮(Syntax Highlighting)

在开始围栏后指定语言名称,渲染器会对代码进行语法高亮。语法高亮是由前端库(如 Prism.js、highlight.js)或服务端处理器实现的,不是 Markdown 本身的功能。

常用语言标识符

语言标识符(常用)备注
Pythonpython / py不区分大小写
JavaScriptjavascript / js
TypeScripttypescript / ts
Shell/Bashbash / sh / shell / zsh
SQLsql
JSONjson
YAMLyaml / yml
HTMLhtml
CSScss / scss / sass / less
Gogo / golang
Rustrust / rs
Javajava
Cc
C++cpp / c++
C#csharp / cs
Swiftswift
Kotlinkotlin / kt
Rubyruby / rb
PHPphp
Dockerfiledockerfile
Markdownmarkdown / md在 Markdown 中显示 Markdown 源码
纯文本(不高亮)text / plaintext或不指定语言
终端输出console / terminalGitHub 特殊样式
TIP语法高亮语言标识符不区分大小写(Pythonpython 都可以)。如果不确定某个语言的标识符,可以查阅 highlight.js 支持语言列表Prism.js 支持语言列表

语法高亮的工作原理

了解语法高亮的原理,有助于理解为什么有时渲染结果不符合预期:

代码文本 ↓ 词法分析(Lexer / Tokenizer) Token 流(关键字、字符串、注释、数字、标识符...) ↓ 根据 Token 类型包裹 <span class="keyword"> 等标签 带标签的 HTML ↓ CSS 样式 颜色高亮的最终展示

因此,语法高亮的质量取决于:

diff 代码块

diff 作为语言标识符,配合 +(新增行)和 -(删除行)前缀,展示代码变更:

```diff
 function greet(name) {
-  console.log("Hello " + name);
+  console.log(`Hello, ${name}!`);
 }

+function farewell(name) {
+  console.log(`Goodbye, ${name}!`);
+}
```
function greet(name) {
- console.log("Hello " + name);
+ console.log(`Hello, ${name}!`);
}
+function farewell(name) {
+ console.log(`Goodbye, ${name}!`);
+}

diff 块的规则

前缀含义颜色
+ 开头新增的行绿色背景
- 开头删除的行红色背景
开头(空格)或无前缀上下文行(未变更)正常颜色
@@ ... @@块头(Hunk Header),标注文件行号蓝色/青色
TIP(diff 的实际应用)在技术文档中,diff 代码块特别适合:① 教程中展示"修改前 vs 修改后";② 配置文件的变更说明;③ 代码重构前后对比。比用注释"修改这行"更直观。

缩进代码块(旧式语法)

CommonMark 规范也支持用 4 个空格(或 1 个制表符)缩进来创建代码块,这是最早的 Markdown 代码块语法:

源码(每行前 4 个空格)
    def hello():
        print("Hello, World!")

    hello()
渲染效果
def hello():
    print("Hello, World!")

hello()
DANGER(强烈不推荐)不要使用缩进代码块:① 不能指定语言,无法语法高亮;② 在列表项中会产生歧义(列表内续接内容也用4个空格缩进);③ 文档中大量使用时很难分辨"这是代码"还是"这是列表续接内容"。请始终使用围栏代码块(三个反引号)。

代码块内的特殊处理

HTML 特殊字符的转义

在围栏代码块中,HTML 特殊字符(如 <>&)会被自动转义为 HTML 实体,所以可以直接写原始代码不用担心 HTML 冲突:

```html
<!-- 模板变量不会被 HTML 解析器处理 -->
<div class="container">
  <p>{{ user.name }}</p>
</div>
```

代码块内不处理 Markdown 语法

围栏代码块内部的任何 Markdown 语法都会被当作字面量显示:

```
**这不会变成加粗**
# 这不会变成标题
[这不会变成链接](url)
- 这不会变成列表
```

展示 Markdown 语法本身

如果需要在文档中展示 Markdown 语法(比如写 Markdown 教程),把 Markdown 代码放在代码块内:

```markdown
# 这是一个标题示例

- 列表项1
- 列表项2

**加粗**和*斜体*
```

特殊语言标识符

console / terminal

用于展示终端命令和输出,GitHub 会给命令行添加特殊样式:

```console
$ npm install
npm warn deprecated package@1.0.0
added 245 packages in 3s

$ npm run build
> my-app@1.0.0 build
> vite build
✓ built in 1.23s
```

math(GitHub LaTeX 代码块)

GitHub 支持用 math 标识符的代码块来渲染 LaTeX 数学公式:

```math
E = mc^2
```

```math
\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
```

mermaid

GitHub 原生支持 Mermaid 图表(详见第8章):

```mermaid
flowchart TD
    A[开始] --> B{条件}
    B -->|是| C[执行]
    B -->|否| D[跳过]
```

代码块与可访问性

在技术文档中,代码块的可访问性也值得关注:

代码块排版最佳实践

1. 正确指定语言

# ❌ 不好:没有语言标识符,无法高亮
```
function hello() {
  console.log("Hello")
}
```

# ✅ 好:有语言标识符
```javascript
function hello() {
  console.log("Hello")
}
```

2. 代码必须可运行

# ❌ 不好:缺少 import,无法直接运行
```python
df = pd.read_csv('data.csv')
```

# ✅ 好:包含必要的 import
```python
import pandas as pd

df = pd.read_csv('data.csv')
print(df.head())
```

3. 长代码块用注释说明关键步骤

```python
# 步骤1:加载预训练模型
model = AutoModel.from_pretrained("bert-base-chinese")

# 步骤2:准备输入数据
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
inputs = tokenizer("这是测试句子", return_tensors="pt")

# 步骤3:获取嵌入向量
with torch.no_grad():
    outputs = model(**inputs)

# outputs.last_hidden_state 包含每个 token 的上下文向量
embeddings = outputs.last_hidden_state
print(f"输出形状: {embeddings.shape}")  # [batch, seq_len, 768]
```

小结

本章要点