跳转到主要内容

代码块

普通 Markdown 围栏加一行属性,就得到文件名标题、精确复制、行号、高亮、换行、折叠与可链接的行。

代码块是普通的 Markdown 围栏,高亮由 Hugo 内置的 Chroma 在构建期完成,浏览器里没有高亮器。用于命令、配置片段与源码:围栏信息行上的 {…} 属性决定标题栏、复制行为、行号与行锚点。图示类围栏(mermaidechartsfiletree 等)不走这条路径,它们各有渲染钩子。

最简例子

源码
```sql
SELECT datname, numbackends FROM pg_stat_database ORDER BY numbackends DESC;
```
SELECT datname, numbackends FROM pg_stat_database ORDER BY numbackends DESC;

没有属性的围栏同样有完整外壳与复制按钮。无标题栏时不渲染空白横条,复制按钮浮在右上角,鼠标悬停或焦点进入块内时出现,触屏设备上始终可见。外壳不显示语言名,lexer 名字只写入 data-language,供样式表与测试使用。

语言标记就是 Chroma 的 lexer 名。diff 围栏用 Chroma 的增删行样式呈现补丁,不需要额外组件:

源码
```diff {title="hugo.yml 的改动"}
 params:
   ui:
-    sidebar_menu_compact: true
+    sidebar_menu_compact: false
     sidebar_menu_foldable: true
```
hugo.yml 的改动
 params:
   ui:
-    sidebar_menu_compact: true
+    sidebar_menu_compact: false
     sidebar_menu_foldable: true

文件名标题

title 给块加一条可见标题栏,通常写文件名或路径。它同时成为这个块的无障碍名称。

源码
```yaml {title="hugo.yml"}
markup:
  goldmark:
    parser:
      attribute:
        block: true
    renderer:
      unsafe: true
```
hugo.yml
markup:
  goldmark:
    parser:
      attribute:
        block: true
    renderer:
      unsafe: true

filenametitle 的历史别名,两个一起写会构建失败。

行号、起始行与高亮

lineNosinline(行号与代码同一列)或 table(行号独立成列,可单独选中不被复制)。lineNoStart 改显示的起始编号。hl_lines 标记要强调的行,计数按围栏内的源码行,从 1 开始,与 lineNoStart 无关。

源码
```ini {title="postgresql.conf" lineNos="inline" lineNoStart=120 hl_lines="2 4-5"}
shared_buffers = 8GB
max_connections = 200
work_mem = 64MB
wal_level = replica
max_wal_senders = 10
```
postgresql.conf
120shared_buffers = 8GB
121max_connections = 200
122work_mem = 64MB
123wal_level = replica
124max_wal_senders = 10

lineNos="table" 把行号放进独立的一列(两种模式下复制按钮都会剔除行号):

源码
```bash {title="部署三条命令" lineNos="table"}
./configure -c rich
./install.yml
pig ext install pg_duckdb
```
部署三条命令
1
2
3
./configure -c rich
./install.yml
pig ext install pg_duckdb

tabWidth 决定制表符展开成几个空格,与 style 一样原样转交 Chroma。本站使用基于 class 的 Chroma 调色板(深浅色各一套),style 只在把 Hugo 切回内联样式模式时才生效。

长行换行

wrap=true 只改变显示:源码不变,复制出来的文本也不变。不加它时长行横向滚动。

源码
```text {title="config/artifacts.env" wrap=true}
ARTIFACT_URL=https://repo.pigsty.io/pkg/infra/v3.6.0/infra-pkg-v3.6.0.el9.x86_64.tgz
CHECKSUM=sha256:6d3dce4f7acb18f586469adcb80ab35f3e859f9837786e151cfbc2b3c0f587b2
```
config/artifacts.env
ARTIFACT_URL=https://repo.pigsty.io/pkg/infra/v3.6.0/infra-pkg-v3.6.0.el9.x86_64.tgz
CHECKSUM=sha256:6d3dce4f7acb18f586469adcb80ab35f3e859f9837786e151cfbc2b3c0f587b2

wrap=true 与表格行号不能共存:行号列与代码列是两个表格单元格,换行后会错位。写在一起构建失败,报错提示改用 lineNos="inline" 或去掉换行。

折叠长代码

collapse=N 让块初始只显示 N 行,底部给一个「显示全部 N 行」按钮。服务器输出完整代码,折叠是浏览器量出第 N 行位置后的视觉裁切:没有 JavaScript 时、读屏器中、打印时代码都是完整的。

源码
```yaml {title="hugo.yml" collapse=8}
baseURL: https://oink.pgsty.com/
title: OINK
defaultContentLanguage: en
languages:
  en:
    languageName: English
    weight: 1
  zh:
    languageName: 简体中文
    weight: 2
params:
  offline_search: true
  ui:
    sidebar_menu_foldable: true
```
hugo.yml
baseURL: https://oink.pgsty.com/
title: OINK
defaultContentLanguage: en
languages:
  en:
    languageName: English
    weight: 1
  zh:
    languageName: 简体中文
    weight: 2
params:
  offline_search: true
  ui:
    sidebar_menu_foldable: true

行数不超过 collapse 时按钮不出现。换行与折叠可以一起用:折叠测量的是第 N 个源码行节点的底边,换行的行不会被截断。

复制内容

默认复制整块源码。终端会话(consoleshell-session 两个 lexer)默认只复制命令:带提示符的行留下,提示符本身与输出行去掉。下面这个块复制出来只有两条命令,没有 $ 也没有输出。

源码
```console
$ pig ext list duckdb
name       version  category
pg_duckdb  1.0.0    OLAP
$ pig ext install pg_duckdb
INFO installing pg_duckdb
```
$ pig ext list duckdb
name       version  category
pg_duckdb  1.0.0    OLAP
$ pig ext install pg_duckdb
INFO installing pg_duckdb

要连提示符与输出一起复制就写 copy="all"。把 copy="command" 用在 bashsh 之类普通 lexer 上会构建失败,因为它们分不出提示符、命令与输出。多行命令请在续行里写出续行提示符(通常是 >),否则那一行会被当成输出而排除。

会话 lexer 的块里一行提示符都没有时,复制按钮报失败:图标转为错误状态,控制台留一条错误,剪贴板不变。它不会退化成复制全文。

copy=false 关掉这一块的复制按钮,用于不应被抄走的反例片段:

源码
```yaml {title="反例:属性行离开了它的块" copy=false}
params:
  ui:
    image_zoom: true   # 错:image_zoom 是一张表,不是布尔值
```
反例:属性行离开了它的块
params:
  ui:
    image_zoom: true   # 错:image_zoom 是一张表,不是布尔值

整站关掉复制用 params.ui.code_copy: false,它优先于每个块自己写的 copy(见配置总览)。复制按钮只有图标,成功与失败会换图标并播报本地化状态;复制内容保留缩进、空行与 Unicode,去掉行号,末尾只留一个换行。

把「看第 3 行」做成链接需要两步:给围栏一个明确的 id,再打开 anchorLineNos=true。行号随即变成锚点链接,锚点是 #<id>-<行号>

源码
```sql {id="ex-explain" title="explain.sql" lineNos="table" anchorLineNos=true}
EXPLAIN (ANALYZE, BUFFERS)
SELECT relname, n_live_tup
FROM pg_stat_user_tables
WHERE n_live_tup > 1000
ORDER BY n_live_tup DESC;
```

跳到 [第 4 行](#ex-explain-4)。
explain.sql
1
2
3
4
5
EXPLAIN (ANALYZE, BUFFERS)
SELECT relname, n_live_tup
FROM pg_stat_user_tables
WHERE n_live_tup > 1000
ORDER BY n_live_tup DESC;

跳到 第 4 行

不写 id 时主题也会生成一个页面内唯一的 ID,但它依赖围栏在页面里的顺序,前面插入一个新围栏就会变。只有作者书写的 id 才是永久链接。ID 不能含空白与控制字符,也不能与页面上其它块的 viewport、标签、面板、标题、行锚点 ID 重复,重复即构建失败。

编号例

写书或长手册时给代码片段编号:numcaption,这个围栏就成了一条 Book「示例」目标,可以被 xref 引用,也会进入全书的示例目录。编号由作者书写,主题不自动计数;id 默认是 eg-<num>

源码
```sql {num="4-1" caption="按表统计膨胀率" #eg-bloat}
SELECT schemaname, relname, n_dead_tup, n_live_tup
FROM pg_stat_user_tables
WHERE n_dead_tup > n_live_tup * 0.2;
```

参见 {{< xref eg="4-1" anchor="eg-bloat" >}}。
示例 4-1 按表统计膨胀率
SELECT schemaname, relname, n_dead_tup, n_live_tup
FROM pg_stat_user_tables
WHERE n_dead_tup > n_live_tup * 0.2;

参见 示例 4-1

numcaption 必须成对出现,只写一个会构建失败;num 与标签页属性 tab 互斥。图、表、公式的编号写法与索引见书籍出版

一组围栏做成标签页

连续几个带 tab 的围栏会在浏览器里合成一个标签页集,第一个围栏上的 group 让它可分享、可同步、可记住选择。

源码
```bash {tab="Homebrew" group="oink-install" value="brew"}
brew install hugo
```
```bash {tab="APT" value="apt"}
sudo apt install hugo
```
Homebrew
brew install hugo
APT
sudo apt install hugo

完整规则(分组语法、URL hash、跨组同步、正文标签页)在标签页

易错写法

  • 在文档里展示 shortcode:围栏不阻止 Hugo 解析,写在代码块里的 {{< tabs >}} 仍会执行。要让它原样显示,在两侧定界符的内侧各加一对注释符号,写成 {{</* tabs */>}},百分号形式对应 {{%/* steps */%}}。本页每一处展示 shortcode 的地方都是这么写的。
  • 围栏里套围栏:外层用四个反引号、内层三个,本页每一段「源码」都是这么写的;内层还有围栏时外层再加一个。
  • 属性写在信息行上:围栏的属性跟在开栏那一行的语言后面,表格与图片的属性才写在块的下一行。写到下一行会变成正文里一段可见的花括号。
  • 未知属性会失败,不会被忽略,错误信息里列出允许的名字。stylesrcdocon* 被拒绝;data-td-code* 前缀以及 data-languagedata-line-countdata-collapse-lines 是主题的保留名,写上去同样构建失败。
  • 列表项里的围栏:缩进要与列表项内容对齐(1. 之后恒定三个空格),否则围栏会脱离列表。

输出形态

输出 呈现
HTML <div class="td-code"> 外壳 + Chroma 的 .highlight/.chroma;复制、折叠按钮在服务器输出里是 hidden,脚本确认可用后才显示
打印 完整代码,去掉复制、折叠、渐隐;长块允许跨页;标题栏保留
Markdown 原样输出源码围栏,连 {…} 属性一起
RSS 静态代码块,无按钮

没有复制或折叠控件的页面不加载 code-block.js;打印、Markdown 与 RSS 输出不加载。

参数参考

开栏那一行、语言之后的 {…} 里,OINK 自己的属性:

title , 非空字符串 , default
可见标题栏(通常是文件名),同时是无障碍名称
filename , 非空字符串 , default
title 的历史别名;两者同时出现构建失败
copy , all command true false , default会话 lexer 为 command,其余为 all
true 等价于 allcommand 只允许 console/shell-session
wrap , 布尔 , defaultfalse
视觉换行,不改源码;与表格行号互斥
collapse , 正整数 , default
初始显示的最大行数;行数不足时不生效
label , 非空字符串 , default由标题派生
无障碍名称,不显示在页面上;与 aria-label 互斥
id , 非空 token , default自动生成
稳定的块 ID 与行锚点前缀;不能含空白
tab , 非空字符串 , default
标签名,见标签页;与 num 互斥
group , ^[a-z][a-z0-9_-]*$ , default
写在一组的第一个围栏上,启用 hash / 同步 / 持久化;需要 tab
value , ^[a-z0-9][a-z0-9_-]*$ , default
分组内每个围栏必填,无分组时禁止;需要 tab
num , [0-9A-Za-z.-]+ , default
编号示例(Book eg);必须与 caption 同时出现
caption , 纯文本 , default
编号示例的说明;必须与 num 同时出现
class , class 列表 , default
追加到 .td-code 根元素
data-* / aria-* / role , 字符串 , default
透传到根元素

titlefilenamelabel 已经为块生成了无障碍名称与 role="group"。它们中的任意一个与 aria-labelaria-labelledbyrole 同时出现都会构建失败;这三个属性只在块没有标题也没有 label 时可以透传。

同一行还能写 Chroma 选项,主题原样转交 Hugo:

lineNos , false inline table , defaultfalse
行号形态;tablewrap=true 互斥
lineNoStart , 正整数 , default1
显示的起始行号,不影响 hl_lines 的计数
hl_lines , 行号与区间 , default
"2 4-5",按围栏内源码行计数
anchorLineNos , 布尔 , defaultfalse
行号变成锚点链接,前缀取自块的 id
tabWidth , 正整数 , defaultHugo 默认
制表符展开的空格数

限制与常见问题

  • 不换高亮器:没有 Shiki、Twoslash、浏览器端高亮,也没有可执行的代码演练场。补丁用 diff 围栏,Chroma 的 .gi/.gd 就是增删行的样式。
  • copy="command" 只认会话 lexer:写在别的语言上是构建错误,不会退化成复制全部。
  • 自动生成的 ID 不是永久链接:要发链接就写 id
  • mermaidmathchemmarkmapplantumlechartsinfographicchecksumsfiletreegallery 不是代码块:它们有各自的渲染钩子,不套这层外壳,也没有复制按钮。
  • 标签页 — 相邻围栏合成标签页的完整规则
  • 引用 — 把仓库里的真实文件当代码块插进来
  • 书籍出版 — 编号示例、交叉引用与示例目录
  • 打印支持 — 长代码在打印里的形态