mermaid-ascii ER画布机制详解:可增长的二维rune网格canvas

发布时间:2026/9/20 7:46:39
mermaid-ascii ER画布机制详解:可增长的二维rune网格canvas
mermaid-ascii ER画布机制详解可增长的二维rune网格canvas【免费下载链接】mermaid-asciiRender Mermaid graphs inside your terminal项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-asciimermaid-ascii 是一个在终端中渲染 Mermaid 图表的开源工具其中 ER 图实体关系图渲染器的核心是一块可增长的二维 rune 网格画布canvas。它不像传统绘图那样“画线”而是先把实体表格“盖章”到网格上再逐格计算连接线该用哪个制表符最终把网格拼回纯文本。读懂这块画布就看懂了 mermaid-ascii 如何保证 ER 图的连线永不重叠、中文标签永不错位。整体流程解析 → 排版 → 盖章 → 连线整个渲染管线非常直白入口在 pkg/er/renderer.go 的Render函数c : canvas{} for _, p : range lay.placed { c.stamp(p.x, p.y, p.lines) // 1. 把实体表格盖到网格 } drawConnectors(c, lay, d, g) // 2. 在网格上绘制关系连线 return c.String() // 3. 网格转回字符串对应该流程的调用链是pkg/render/render.go#L9-L37 统一分发 → pkg/render/diagram.go#L97-L115 适配 ER 类型 →er.Render完成绘制。解析阶段由 pkg/er/parser.go 完成把erDiagram源码变成实体Entity和关系Relationship两个列表。canvas 本体一个会自己变大的 [][]rune画布的全部数据结构只有几行定义在 pkg/er/layout.go#L12-L14// canvas is a growable 2D grid of runes that boxes are stamped onto // and connectors are drawn across. type canvas struct { rows [][]rune }它有三个方法各自解决一个问题方法作用关键点ensure(x, y)按需扩容行不够就补空行列不够就补空格先画图后扩容无需预估尺寸set(x, y, r)写一个格子负坐标直接忽略越界自动触发ensureat(x, y)读一个格子越界返回空格不会 panic这种“稀疏 懒增长”的设计让布局代码可以先随便放、随便画画布自己会跟着长最后再统一导出——这是它叫growable可增长的原因。导出时String()负责把网格拼回文本逻辑见 pkg/er/layout.go#L58-L71每行去掉哨兵格、裁掉行尾空格后加换行。宽字符对齐让中文、emoji 不“串行”终端里一个中文字符占两列而 ASCII 字符只占一列。如果按“字符”计数中文标签会直接打乱整列对齐。canvas 的解法是按显示宽度推进列号stamp方法pkg/er/layout.go#L44-L56每写一个 rune 就查runewidth.RuneWidth双宽字符额外占一格并写入一个值为0的哨兵标记“这格属于前一个字符不要渲染”。String()导出时跳过哨兵格最终文本在终端里的列就和网格一一对应。所以 mermaid-ascii 的 ER 图可以放心使用中文实体名、中文关系标签测试数据 cmd/testdata/er/cjk_entities.txt 专门覆盖了这个场景以及带组合记号的 Unicode 字符。排版实体表格怎么排、连线走哪条“车道”placeEntitiespkg/er/layout.go#L106-L214负责把实体摆成一个接近正方形的网格并预留“走线区gutter”每行 gutter 的宽度 关系数 最长标签宽 5。每条关系线独占一条全局车道lane不同关系永远不在同一行走线标签因此永不打架每个实体盒子的宽度按“度数”撑开连的线越多盒子越宽保证每条线在盒子边缘有自己的“停靠列”相邻列间隔 4 格留出 2 格的鸦脚crows-foot基数标记空间停靠点按奇偶吸附底边用偶数列、顶边用奇数列见 pkg/er/layout.go#L297-L315上下叠放的两个盒子伸出的短线永远不会共用同一列、视觉上粘成一条。这就是为什么 mermaid-ascii 的 ER 图里多条关系线即使密集交叉也总能看清每条线走的是哪条“车道”。连线绘制方向位掩码 一次性合成连线不是直接写字符而是先在一层overlay上记录“这个格子朝哪些方向有线”用 4 个方向位dN/dS/dE/dWpkg/er/layout.go#L228-L235标记每个格子的邻居连接实线识别关系--与虚线非识别关系..分开记账保证识别线穿过虚线时不被“传染”最后compositepkg/er/layout.go#L633-L662逐格查位掩码用glyphFor映射到对应制表符两端转折是└┘┌┐三向是├┤┬┴四向交叉则是┼。这里有个关键保护composite只往空格子上写线if c.at(x, y) ! { return }所以连线永远不会划伤已经盖章的实体盒子线盒交汇的 T 型接头由setAttachTee单独补上若格子原本就是列分隔的 T 字则自动合并成┼pkg/er/layout.go#L408-L419。运行效果速览用一个带属性的 ER 示例跑 mermaid-ascii输出类似这样ASCII 模式erDiagram CUSTOMER ||--o{ ORDER : places CUSTOMER { string name PK string email } ORDER { int id PK decimal total }--------------------- ---------------------- | CUSTOMER | | ORDER | ------------------- -------------------- | string | name | PK | | int | id | PK | | string | email | | | decimal | total | | ------------------ ------------------- ||-----------places------------o{可以看到places标签完整落在 gutter 车道上鸦脚标记||和o{各自紧贴盒子边缘——这正是 canvas 逐格计算的结果。更多黄金样本可以参考 cmd/testdata/er/ 目录下的 70 余个用例例如 cmd/testdata/er/matrix_one_or_more_to_one_or_more.txt。小结三句话记住 canvas 机制网格懒增长rows [][]rune先画后扩负坐标/越界都安全布局代码无心理解总尺寸按显示宽度对齐双宽字符带哨兵格让 CJK 与 ASCII 在同一个格网里精确对齐位掩码合成连线方向位 空格保护 统一字形映射使实线、虚线、交叉、T 型接头全部自动正确。想动手验证可以直接看 pkg/er/layout.go 与 pkg/er/renderer.go 两个文件配合 pkg/er/layout_smoke_test.go 里的测试快速观察每一步网格的状态。这套“盖印 走线 合成”的思路也正是 mermaid-ascii 处理 graph、sequence 等其它图表类型的通用底座。【免费下载链接】mermaid-asciiRender Mermaid graphs inside your terminal项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-ascii创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考