Skip to content

功能全览:自动格式化的魔法

学完你能做什么

  • 了解插件的 8 大核心功能
  • 知道哪些场景适合用这个插件
  • 明白插件的边界在哪里(不能做什么)

你现在的困境

插件信息

本插件全名为 @franlol/opencode-md-table-formatter,以下简称"表格格式化插件"。

AI 生成的 Markdown 表格经常是这样的:

markdown
| 名称 | 描述 | 状态 |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成 |
| API | 接口文档 | 🚧 进行中 |

列宽参差不齐,看着难受。手动调整?每次 AI 生成新表格都要调一遍,太累了。

什么时候用这一招

  • AI 生成了 Markdown 表格,你想让它更整齐
  • 你开启了 OpenCode 的隐藏模式(Concealment Mode),表格对齐总是出问题
  • 你懒得手动调整表格列宽

核心思路

这个插件的工作原理很简单:

AI 生成文本 → 插件检测表格 → 验证结构 → 格式化 → 返回美化后的文本

它挂载在 OpenCode 的 experimental.text.complete 钩子上,AI 每次生成完文本,插件就自动处理。你不需要手动触发,全程无感。

8 大核心功能

1. 自动表格格式化

插件会自动检测 AI 生成文本中的 Markdown 表格,统一列宽,让表格整齐美观。

格式化前

markdown
| 名称 | 描述 | 状态 |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成 |
| API | 接口文档 | 🚧 进行中 |

格式化后

markdown
| 名称         | 描述         | 状态       |
|--- | --- | ---|
| **用户管理** | 管理系统用户 | ✅ 完成    |
| API          | 接口文档     | 🚧 进行中  |

触发条件

插件挂载在 experimental.text.complete 钩子上,AI 生成文本完成后自动触发,无需手动操作。

2. 隐藏模式兼容

OpenCode 默认开启隐藏模式(Concealment Mode),会隐藏 Markdown 符号(如 ***~~)。

普通的表格格式化工具不考虑这一点,计算宽度时会把 ** 也算进去,导致对齐错位。

这个插件专门为隐藏模式优化:

  • 计算宽度时,剥离 **粗体***斜体*~~删除线~~ 等符号
  • 输出时保留原始 Markdown 语法
  • 最终效果:隐藏模式下表格完美对齐
技术细节:宽度计算逻辑
typescript
// 剥离 Markdown 符号(用于宽度计算)
visualText = visualText
  .replace(/\*\*\*(.+?)\*\*\*/g, "$1") // ***粗斜体*** → 文本
  .replace(/\*\*(.+?)\*\*/g, "$1")     // **粗体** → 粗体
  .replace(/\*(.+?)\*/g, "$1")         // *斜体* → 斜体
  .replace(/~~(.+?)~~/g, "$1")         // ~~删除线~~ → 删除线

源码位置:index.ts:181-185

3. 对齐支持

支持 Markdown 表格的三种对齐方式:

语法对齐方式效果
---:---左对齐文本靠左(两种语法效果相同)
:---:居中文本居中
---:右对齐文本靠右

示例

markdown
| 左对齐 | 居中 | 右对齐 |
|--- | --- | ---|
| 文本 | 文本 | 文本 |

格式化后,每列会按指定方式对齐,分隔行会根据对齐方式重新生成。

4. 嵌套 Markdown 处理

表格单元格里可能有嵌套的 Markdown 语法,比如 ***粗斜体***

插件使用多轮正则算法,从外到内逐层剥离:

***粗斜体*** → **粗斜体** → *粗斜体* → 粗斜体

这样即使嵌套多层,宽度计算也是准确的。

5. 代码块保护

行内代码(用反引号包裹)里的 Markdown 符号应该保持原样,不被剥离。

比如 `**bold**`,用户看到的就是 **bold** 这 8 个字符,而不是 bold 这 4 个字符。

插件会先提取代码块内容,剥离其他部分的 Markdown 符号后,再把代码块内容放回去。

技术细节:代码块保护逻辑
typescript
// 第 1 步:提取并保护行内代码
const codeBlocks: string[] = []
let textWithPlaceholders = text.replace(/`(.+?)`/g, (match, content) => {
  codeBlocks.push(content)
  return `\x00CODE${codeBlocks.length - 1}\x00`
})

// 第 2 步:剥离非代码部分的 Markdown 符号
// ...

// 第 3 步:恢复行内代码内容
visualText = visualText.replace(/\x00CODE(\d+)\x00/g, (match, index) => {
  return codeBlocks[parseInt(index)]
})

源码位置:index.ts:168-193

6. 边界情况处理

插件能正确处理各种边界情况:

场景处理方式
Emoji 表情使用 Bun.stringWidth 正确计算显示宽度
Unicode 字符中文、日文等宽字符正确对齐
空单元格填充空格到最小宽度(3 字符)
超长内容正常处理,不截断

7. 静默操作

插件在后台静默运行:

  • 无日志输出:不会在控制台打印任何信息
  • 错误不中断:即使格式化失败,也不会影响 AI 的正常输出

如果格式化过程中出错,插件会保留原文,并在末尾添加一条 HTML 注释:

markdown
<!-- table formatting failed: [错误信息] -->

8. 验证反馈

插件会验证表格结构是否有效。无效的表格不会被格式化,而是保留原样,并添加提示:

markdown
<!-- table not formatted: invalid structure -->

有效表格的要求

  • 至少 2 行(含分隔行)
  • 所有行的列数一致
  • 必须有分隔行(格式:|---|---|

插件的边界

不支持的场景

  • HTML 表格:只处理 Markdown 管道表格(| ... |
  • 多行单元格:含 <br> 标签的单元格不支持
  • 无分隔行表格:必须有 |---|---| 分隔行
  • 无表头表格:必须有表头行

检查点

完成本课后,你应该能回答:

  • [ ] 插件是如何自动触发的?(答:experimental.text.complete 钩子)
  • [ ] 为什么需要"隐藏模式兼容"?(答:隐藏模式会隐藏 Markdown 符号,影响宽度计算)
  • [ ] 行内代码里的 Markdown 符号会被剥离吗?(答:不会,代码内的 Markdown 符号会被完整保留)
  • [ ] 无效表格会怎么处理?(答:保留原样,添加错误注释)

本课小结

功能说明
自动格式化AI 生成完文本自动触发,无需手动操作
隐藏模式兼容正确计算 Markdown 符号隐藏后的显示宽度
对齐支持左对齐、居中、右对齐
嵌套 Markdown多轮正则剥离,支持嵌套语法
代码块保护行内代码中的符号保持原样
边界情况Emoji、Unicode、空单元格、超长内容
静默操作无日志,错误不中断
验证反馈无效表格添加错误注释

下一课预告

下一课我们深入 隐藏模式原理

你会学到:

  • OpenCode 隐藏模式的工作原理
  • 插件如何正确计算显示宽度
  • Bun.stringWidth 的作用

附录:源码参考

点击展开查看源码位置

更新时间:2026-01-26

功能文件路径行号
插件入口index.ts9-23
表格检测index.ts58-61
表格验证index.ts70-88
宽度计算(隐藏模式)index.ts161-196
对齐方式解析index.ts141-149
代码块保护index.ts168-173

关键常量

  • colWidths[col] = 3:列最小宽度为 3 字符(index.ts:115

关键函数

  • formatMarkdownTables():主处理函数,格式化文本中的所有表格
  • getStringWidth():计算字符串显示宽度,剥离 Markdown 符号
  • isValidTable():验证表格结构是否有效