json-joy
Version:
Collection of libraries for building collaborative editing apps.
75 lines (74 loc) • 2.82 kB
JavaScript
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;
}
}
}