52 lines
2.1 KiB
TypeScript
52 lines
2.1 KiB
TypeScript
import type { BlockView } from './types'
|
|
|
|
/**
|
|
* 同一时刻能编辑的几段源码不重叠,同一行不会同时在两处能改:
|
|
* ① 块编辑时,编辑范围和它重叠的子孙块不画(库规则的卡片编辑整个类,留下的方法也在这段里;
|
|
* 一行写完的 `if x: return 1`,子块和头部在同一行),它们下面的子孙也跟着不画;
|
|
* ② 点「编辑」时,编辑范围和这块重叠的别的块退出编辑。
|
|
*/
|
|
|
|
type Lines = Pick<BlockView, 'startLine' | 'endLine'>
|
|
|
|
function overlaps(a: Lines, b: Lines): boolean {
|
|
return a.startLine <= b.endLine && b.startLine <= a.endLine
|
|
}
|
|
|
|
/** 正在编辑的块盖住、不画的块 → 盖住它的那块(页面上画着的、正在编辑的祖先):跳到定义落在被盖住的块上时改闪它。 */
|
|
export function coveredByEditing(blocks: readonly BlockView[], editing: ReadonlySet<string>): Map<string, string> {
|
|
const covered = new Map<string, string>()
|
|
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<BlockView> {
|
|
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]
|
|
}
|