3.24

#diary #2026/3

由于不知名原因又和 Markdown 列表项末尾必须添加空行才不会把紧接着的另一个段落被识别为该列表项的内容的这个烦人特性较真了起来。狠狠地学习了一番。

Markdown 渲染本质上还是转换成 HTML 再渲染(Obsidian 内按 Ctrl + Shift + I 甚至能看到类似于 Chrome inspect 的界面!)。业界普遍的 Markdown 转 HTML 的格式规范叫 CommonMark,其中关于 list item 这个 block container 有一条特殊的规则叫 lazy continuation lines,这条规则允许部分删除这些行的前导缩进:可以被解析为 "不是段落第一行的段落行" 的行。这导致书写如下段落时必须添加显式的空行来区分一行究竟属于父级还是属于子级

- list item A intro
    - list item A member 1
    - list item A member 2
	list item A summary
- list item B intro

// interpretation A (intuitional)
<ul>
	<li>
		<p>list item A intro</p>
		<ul>
			<li>list item A member 1</li>
			<li>list item A member 2</li>
		</ul>
		<p>list item A summary</p>
	</li>
	<li>
		<p>list item B intro</p>
	</li>
</ul>

// interpretation B (actually)
<ul>
	<li>
		<p>list item A intro</p>
		<ul>
			<li>list item A member 1</li>
			<li>list item A member 2<br>list item A summary</li>
		</ul>
	</li>
	<li>
		<p>list item B intro</p>
	</li>
</ul>

可以用 Dingus 来在线查看 Markdown 依据 CommonMark 的渲染结果和对应 HTML。

本质上是因为在 markdown 的解析器在识别 list item 时的判断停止的,对于顶级的 list item,CommonMark 的规则是

实际上的操作过程应该是嵌套、贪心地完成的。

那么为什么 Obsidian 可以正常地渲染呢,因为 Obsidian 用的是一个超级魔改版的 Markdown 解释器,支持了很多新奇的功能比如 callout、backlinks、highlights,在大量基础细节的处理上也与 CommonMark 截然不同。然鹅,Digital Garden 的默认仓库采用的是 Eleventy,Eleventy 默认采用严格遵循 CommonMarks 的 markdown-it 解析引擎,虽然 Digital Garden 为了处理各种 Obsidian 的特性引入了很多 Eleventy 的插件并辅以额外的处理来实现了 Obsidian 各种特性的转换,但是基本的文本处理仍然是按照 CommonMarks 的。

所以说对于博客的相关 .md 文件,在基本的文本规范上(不考虑复杂特性)我们应当使用一种能够同时兼容 Obsidian 和 CommonMarks 二者语法,并且同时保持在 Obsidian 内部渲染和转为 HTML 网页渲染的美观性的一种规范。

仅仅考虑目前的困境,一个可取的规范是在列表的前后都添加空行。此时的结构语义表达和 HTML 其实有点像。

Linter 有一个在列表前后均添加空格的格式化功能,但是它不能处理嵌套列表,因为 CommonMarks 的 list 分为 loose list 和 tight list,添加空行会导致潜在的从 tight list 到 loose list 的语义改变。所以一键格式化所有之前文件的计划只能暂时搁置。


把 Obsidian 的主题换成了 Minimal。


flashcards 的多行卡片的首尾内容默认情况下要求不能分行,但是由于上面提到的列表换行问题,有时候空行几乎是在所难免的。不过,在翻配置的时候发现了可以设定多行 flashcards 尾段的终止标识符,于是设成了 ***,既是终止符又是分割线,我简直是天才!还有一些意外的发现,以前敲 ---=== 的时候会出现莫名地格式爆炸,结合今天学到的 CommonMarks 规范发现这其实是 setext heading 语法,一种十分丑陋的 heading 语法。如果多行 flashcards 的终止符设定为 --- 或者 === 的话,只要其上不加空行就会触发 setext heading 语法,所以只能用 *** 或者一些分隔符的变体比如 - - -,但最简单的方案仍然是 ***


偶然发现 Obsidian 竟然有任务 / 待办功能的原生支持,还有强大的 Task 插件,看起来可以完全替代 TickTick。没想到 Obsidian 竟然可以集成笔记管理、博客更新、辅助记忆、日程待办所有这些功能于一体。


今天下雨了,跑步大业出师不利。

张雪峰因为过劳加跑步去世了。


footnote / callout 试验发疯现场

天上有一个在飞的城堡[1]


  1. 哈尔的移动城堡 ↩︎