大纲笔记中 Enter 键行为的分岔

在大纲笔记的一个节点里,按下 Enter,会有什么效果?

这取决于两件事:光标停在文字的哪个位置,以及这个块当时是什么状态。

图 1:同一个块,三个光标位置

先约定三个词。

  • 块:一行文字,可以带子块。这是两家共有的唯一单位。

  • 光标位置:按字符算。块首是 0,块中是 0 与长度之间,块尾等于这一行的长度。

  • 根:当前这一段最上面那个块。它没有父块,所以「同级」这个概念对它不成立。

下面按光标位置走。

一、块首:上面多一个空块

光标在文字最前面,按 Enter。

图 2:块首按 Enter,上方多一个空块

表现
Roam上方插入一个空块,光标进新块
Logseq上方插入一个空块,光标进新块

这一格没有分歧。效果等于「在上面开一行」:原块一个字都不改,子树也不动,只是父块的子块列表里多了一项,位置在原块之前。

有一个边界要单独说:块本身是空的、又缩进很深时,走的不是这条,是反缩进(见第四节)。所以「块首」这个判据只在块里有文字时才单独成立。

二、块中:唯一一格两家不一样

光标在文字中间,按 Enter——把一行分成两行。这是两家唯一给出不同结果的地方。

图 3:块中按 Enter,两家在「有子块」时分开

分水岭是「这个块有没有子块」。

场景RoamLogseq
有子块(展开)同级拆:左半抽成上方新块,原块留右半和子树右半成为第一个子块,子树留原块
叶子(无子块)同级拆右半成为同级
有子块但已折叠同级拆(折叠不改变这一格)右半成为同级

「同级拆」和「拆成子块」在视觉上的差别是这样的。

同级拆:后半句和前半句并排,子块仍挂在后半句下面。

拆成子块:后半句往右缩一层,原来那些子块被推到它后面。

所以两种结果里「哪一半是新块」也不同。

Roam:新块在上方,拿左半;原块留下右半和子树。

Logseq:原块留下左半和子树;右半钻进去成为第一个子块。

光标落点也不一样:Roam 留在原块(下半)的开头,Logseq 到新块(子块)的开头。

两家都保持一个不变量:子树始终留在原来那个块上,不跟着被切走的那一半换父块。差别只是「原来那个块」最后装的是左半还是右半。

叶子块这一行容易被记错:Logseq 的「钻一层」只在已经有子块时发生。没有子块的块,它和 Roam 一样落到同级。所以不能说「Logseq 总是把后半句变成子块」。

三、块尾:新块落在光标下方哪一层

光标在文字末尾,按 Enter。

图 4:块尾按 Enter,看这一块有没有子块

场景RoamLogseq
有子块,展开新块成为第一个子块新块成为第一个子块
有子块,折叠同级空块(按分支形状推断)同级空块
无子块同级空块同级空块

这一格两家一致,而且它和第二节的直觉相反:块中按 Enter 时,有子块的块在 Logseq 那里会把后半句变成子块;块尾按 Enter 时,两家都会把新块变成子块。同一个「有子块」的条件,在两格里指向同一件事——新内容进入下一层。

「第一个子块」而不是「最后一个」这一点,在视觉上意味着:新块紧贴在父块下面,把原有子块往下推。如果子树很长,用户接下来写的内容会出现在树的最上方,而不是树的最下方。

折叠态两家都改走同级。Logseq 的折叠判断在源码里是显式的;Roam 这一格是按它的分支形状推断的,没有逐帧验证。

四、空块:不建块,少一层

块是空的,按 Enter。

图 5:空块反缩进,以及页名在两家分别是什么

场景RoamLogseq
空块,缩进 ≥ 2 层反缩进一层,不建块反缩进一层,不建块
空块,顶层内容块另有分支,方向未逐条复刻走「最后顶层块」分支,细节未逐条复现
空块,是根会把整条搬进子块、清空根文本页标题不在块里,这一格不适用

这条把 Enter 变成了 Shift+Tab。效果是「退出这一层」,而且只在缩进 ≥ 2 层时触发;到顶层再按,不会继续往上。

它的存在说明一件事:Enter 不总是「新建」。在一种特定状态下,它的语义是「往上走一层」。这也是为什么「按 Enter 之后光标在哪」必须按分支分别定义,不能用一个统一的规则去推。

五、有选区时:先删,再断

选中一段文字,按 Enter。

图 6:有选区时按 Enter,先删掉再在起点断开

表现
Roam按选区的起点与终点切,选区不留在结果里
Logseq同样用起止两点切,选区不留在结果里

结果一致:abcdefghij 里选中 cdef 再按 Enter,得到 ab 与 ghij,cdef 消失。

这一格值得单列,是因为它和「光标」这一维度的关系很别扭:选区有起点和终点两个位置,而 Enter 的落点判定只认一个位置。如果不先把选区消掉,判据拿到的就只是「起点」,结果会变成 ab 加 cdefghij——选区原样留着,等于把「我要删掉这段」的操作变成了「我要在这里断一下」。

跨块选区(从 A 块拖到 B 块)和页名上的选区,两家都没有逐条取证。

这里要提一下,思源笔记的行为是:不对选区进行消除,相当于在块尾按 Enter。

总表

#场景RoamLogseq
1块首(有文字)上方空块上方空块
2块中,有子块同级拆(左半成上方新块)右半成首个子块
3块中,叶子同级拆同级
4块中,有子块但折叠同级拆同级
5块尾,有子块展开首个子块首个子块
6块尾,折叠同级空块(推断)同级空块
7块尾,叶子同级空块同级空块
8空块,缩进 ≥ 2 层反缩进反缩进
9页名上按 Enter不适用不适用
10有选区先删再断先删再断
11Shift+Enter软换行软换行