大纲笔记中 Enter 键行为的分岔
在大纲笔记的一个节点里,按下 Enter,会有什么效果?
这取决于两件事:光标停在文字的哪个位置,以及这个块当时是什么状态。

先约定三个词。
块:一行文字,可以带子块。这是两家共有的唯一单位。
光标位置:按字符算。块首是 0,块中是 0 与长度之间,块尾等于这一行的长度。
根:当前这一段最上面那个块。它没有父块,所以「同级」这个概念对它不成立。
下面按光标位置走。
一、块首:上面多一个空块
光标在文字最前面,按 Enter。

| 表现 | |
|---|---|
| Roam | 上方插入一个空块,光标进新块 |
| Logseq | 上方插入一个空块,光标进新块 |
这一格没有分歧。效果等于「在上面开一行」:原块一个字都不改,子树也不动,只是父块的子块列表里多了一项,位置在原块之前。
有一个边界要单独说:块本身是空的、又缩进很深时,走的不是这条,是反缩进(见第四节)。所以「块首」这个判据只在块里有文字时才单独成立。
二、块中:唯一一格两家不一样
光标在文字中间,按 Enter——把一行分成两行。这是两家唯一给出不同结果的地方。

分水岭是「这个块有没有子块」。
| 场景 | Roam | Logseq |
|---|---|---|
| 有子块(展开) | 同级拆:左半抽成上方新块,原块留右半和子树 | 右半成为第一个子块,子树留原块 |
| 叶子(无子块) | 同级拆 | 右半成为同级 |
| 有子块但已折叠 | 同级拆(折叠不改变这一格) | 右半成为同级 |
「同级拆」和「拆成子块」在视觉上的差别是这样的。
同级拆:后半句和前半句并排,子块仍挂在后半句下面。
拆成子块:后半句往右缩一层,原来那些子块被推到它后面。
所以两种结果里「哪一半是新块」也不同。
Roam:新块在上方,拿左半;原块留下右半和子树。
Logseq:原块留下左半和子树;右半钻进去成为第一个子块。
光标落点也不一样:Roam 留在原块(下半)的开头,Logseq 到新块(子块)的开头。
两家都保持一个不变量:子树始终留在原来那个块上,不跟着被切走的那一半换父块。差别只是「原来那个块」最后装的是左半还是右半。
叶子块这一行容易被记错:Logseq 的「钻一层」只在已经有子块时发生。没有子块的块,它和 Roam 一样落到同级。所以不能说「Logseq 总是把后半句变成子块」。
三、块尾:新块落在光标下方哪一层
光标在文字末尾,按 Enter。

| 场景 | Roam | Logseq |
|---|---|---|
| 有子块,展开 | 新块成为第一个子块 | 新块成为第一个子块 |
| 有子块,折叠 | 同级空块(按分支形状推断) | 同级空块 |
| 无子块 | 同级空块 | 同级空块 |
这一格两家一致,而且它和第二节的直觉相反:块中按 Enter 时,有子块的块在 Logseq 那里会把后半句变成子块;块尾按 Enter 时,两家都会把新块变成子块。同一个「有子块」的条件,在两格里指向同一件事——新内容进入下一层。
「第一个子块」而不是「最后一个」这一点,在视觉上意味着:新块紧贴在父块下面,把原有子块往下推。如果子树很长,用户接下来写的内容会出现在树的最上方,而不是树的最下方。
折叠态两家都改走同级。Logseq 的折叠判断在源码里是显式的;Roam 这一格是按它的分支形状推断的,没有逐帧验证。
四、空块:不建块,少一层
块是空的,按 Enter。

| 场景 | Roam | Logseq |
|---|---|---|
| 空块,缩进 ≥ 2 层 | 反缩进一层,不建块 | 反缩进一层,不建块 |
| 空块,顶层内容块 | 另有分支,方向未逐条复刻 | 走「最后顶层块」分支,细节未逐条复现 |
| 空块,是根 | 会把整条搬进子块、清空根文本 | 页标题不在块里,这一格不适用 |
这条把 Enter 变成了 Shift+Tab。效果是「退出这一层」,而且只在缩进 ≥ 2 层时触发;到顶层再按,不会继续往上。
它的存在说明一件事:Enter 不总是「新建」。在一种特定状态下,它的语义是「往上走一层」。这也是为什么「按 Enter 之后光标在哪」必须按分支分别定义,不能用一个统一的规则去推。
五、有选区时:先删,再断
选中一段文字,按 Enter。

| 表现 | |
|---|---|
| Roam | 按选区的起点与终点切,选区不留在结果里 |
| Logseq | 同样用起止两点切,选区不留在结果里 |
结果一致:abcdefghij 里选中 cdef 再按 Enter,得到 ab 与 ghij,cdef 消失。
这一格值得单列,是因为它和「光标」这一维度的关系很别扭:选区有起点和终点两个位置,而 Enter 的落点判定只认一个位置。如果不先把选区消掉,判据拿到的就只是「起点」,结果会变成 ab 加 cdefghij——选区原样留着,等于把「我要删掉这段」的操作变成了「我要在这里断一下」。
跨块选区(从 A 块拖到 B 块)和页名上的选区,两家都没有逐条取证。
这里要提一下,思源笔记的行为是:不对选区进行消除,相当于在块尾按 Enter。
总表
| # | 场景 | Roam | Logseq |
|---|---|---|---|
| 1 | 块首(有文字) | 上方空块 | 上方空块 |
| 2 | 块中,有子块 | 同级拆(左半成上方新块) | 右半成首个子块 |
| 3 | 块中,叶子 | 同级拆 | 同级 |
| 4 | 块中,有子块但折叠 | 同级拆 | 同级 |
| 5 | 块尾,有子块展开 | 首个子块 | 首个子块 |
| 6 | 块尾,折叠 | 同级空块(推断) | 同级空块 |
| 7 | 块尾,叶子 | 同级空块 | 同级空块 |
| 8 | 空块,缩进 ≥ 2 层 | 反缩进 | 反缩进 |
| 9 | 页名上按 Enter | 不适用 | 不适用 |
| 10 | 有选区 | 先删再断 | 先删再断 |
| 11 | Shift+Enter | 软换行 | 软换行 |