UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

75 lines (74 loc) 2.82 kB
import { Block } from './Block'; import { commonLength } from '../util/commonLength'; import { printTree } from 'tree-dump/lib/printTree'; import { LeafBlock } from './LeafBlock'; import { Range } from '../rga/Range'; import { CommonSliceType } from '../slice'; /** * A *fragment* represents a structural slice of a rich-text document. A * fragment can be bound to a specific range of text contents, however it * always constructs a tree of {@link Block}s, which represent the nested * structure of the text contents. */ export class Fragment extends Range { txt; root; constructor(txt, start, end) { super(txt.str, start, end); this.txt = txt; this.root = new Block(txt, [], void 0, start, end); } // ------------------------------------------------------------------- export toJson() { const node = this.root.toJson(); node[0] = ''; return node; } // ---------------------------------------------------------------- Printable toString(tab = '') { return 'Fragment' + printTree(tab, [(tab) => this.root.toString(tab)]); } // ----------------------------------------------------------------- Stateful hash = 0; refresh() { this.build(); return (this.hash = this.root.refresh()); } insertBlock(parent, path, marker, end = this.end) { const txt = this.txt; const common = commonLength(path, parent.path); const start = marker ? marker : this.start; while (parent.path.length > common && parent.parent) parent = parent.parent; while (parent.path.length + 1 < path.length) { const block = new Block(txt, path.slice(0, parent.path.length + 1), void 0, start, end); block.parent = parent; parent.children.push(block); parent = block; } const block = new LeafBlock(txt, path, marker, start, end); block.parent = parent; parent.children.push(block); return block; } build() { const { root } = this; root.children = []; let parent = this.root; const txt = this.txt; const overlay = txt.overlay; const iterator = overlay.markerPairs0(this.start, this.end); let pair; while ((pair = iterator())) { const [p1, p2] = pair; const skipFirstVirtualBlock = !p1 && this.start.isAbsStart() && p2 && p2.viewPos() === 0; if (skipFirstVirtualBlock) continue; const type = p1 ? p1.type() : CommonSliceType.p; const path = type instanceof Array ? type : [type]; const block = this.insertBlock(parent, path, p1, p2); if (block.parent) parent = block.parent; } } }