import type { BlockView } from './types' /** * 同一时刻能编辑的几段源码不重叠,同一行不会同时在两处能改: * ① 块编辑时,编辑范围和它重叠的子孙块不画(库规则的卡片编辑整个类,留下的方法也在这段里; * 一行写完的 `if x: return 1`,子块和头部在同一行),它们下面的子孙也跟着不画; * ② 点「编辑」时,编辑范围和这块重叠的别的块退出编辑。 */ type Lines = Pick function overlaps(a: Lines, b: Lines): boolean { return a.startLine <= b.endLine && b.startLine <= a.endLine } /** 正在编辑的块盖住、不画的块 → 盖住它的那块(页面上画着的、正在编辑的祖先):跳到定义落在被盖住的块上时改闪它。 */ export function coveredByEditing(blocks: readonly BlockView[], editing: ReadonlySet): Map { const covered = new Map() const visit = (list: readonly BlockView[], editors: readonly BlockView[], owner: string | undefined): void => { for (const block of list) { const by = owner ?? editors.find(editor => overlaps(editor, block))?.id if (by !== undefined) { covered.set(block.id, by) visit(block.children, editors, by) } else { visit(block.children, editing.has(block.id) ? [...editors, block] : editors, undefined) } } } visit(blocks, [], undefined) return covered } function* allBlocks(blocks: readonly BlockView[]): Generator { for (const block of blocks) { yield block yield* allBlocks(block.children) } } /** 点了 id 这块的「编辑」之后正在编辑的块:加上它,编辑范围和它重叠的别的块退出编辑。 */ export function startEditing(blocks: readonly BlockView[], editing: readonly string[], id: string): string[] { const byId = new Map([...allBlocks(blocks)].map(block => [block.id, block])) const target = byId.get(id) const others = editing.filter((other) => { const block = byId.get(other) return other !== id && !(target && block && overlaps(target, block)) }) return [...others, id] }