All files / json-crdt-extensions/quill-delta QuillDeltaNode.ts

98.03% Statements 50/51
100% Branches 14/14
83.33% Functions 5/6
97.87% Lines 46/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 8147x 47x 47x 47x 47x 47x 47x 47x             47x     282x 282x 282x       283x       1223x       282x           282x 282x     2145x 2145x 2145x 1178x   1178x   1178x 7219x 7219x 7219x 7219x 9768x 9768x   7219x 5134x 34x 34x 34x 34x 34x   5100x 5100x 1558x 1558x     3542x 3542x 3542x     1178x 1178x 1178x      
import {isEmpty} from '@jsonjoy.com/util/lib/isEmpty';
import {deepEqual} from '@jsonjoy.com/util/lib/json-equal/deepEqual';
import {Peritext} from '../peritext';
import {ExtensionId} from '../constants';
import {MNEMONIC, QuillConst} from './constants';
import {ExtNode} from '../../json-crdt/extensions/ExtNode';
import {getAttributes} from './util';
import {updateRga} from '../../json-crdt/hash';
import type {StrNode} from '../../json-crdt/nodes/str/StrNode';
import type {ArrNode} from '../../json-crdt/nodes/arr/ArrNode';
import type {StringChunk} from '../peritext/util/types';
import type {OverlayTuple} from '../peritext/overlay/types';
import type {QuillDataNode, QuillDeltaAttributes, QuillDeltaOp, QuillDeltaOpInsert} from './types';
 
export class QuillDeltaNode extends ExtNode<QuillDataNode> {
  public readonly txt: Peritext<string>;
 
  constructor(public readonly data: QuillDataNode) {
    super(data);
    this.txt = new Peritext<string>(data.doc, this.text(), this.slices());
  }
 
  public text(): StrNode<string> {
    return this.data.get(0)!;
  }
 
  public slices(): ArrNode {
    return this.data.get(1)!;
  }
 
  // ------------------------------------------------------------------ ExtNode
  public readonly extId = ExtensionId.quill;
 
  public name(): string {
    return MNEMONIC;
  }
 
  private _view: QuillDeltaOp[] = [];
  private _viewHash: number = -1;
 
  public view(): QuillDeltaOp[] {
    const overlay = this.txt.overlay;
    const hash = updateRga(overlay.refresh(true), this.txt.str);
    if (hash === this._viewHash) return this._view;
    const ops: QuillDeltaOp[] = [];
    let chunk: undefined | StringChunk;
    const nextTuple = overlay.tuples0(undefined);
    let tuple: OverlayTuple<string> | undefined;
    while ((tuple = nextTuple())) {
      const [p1, p2] = tuple;
      const attributes: undefined | QuillDeltaAttributes = getAttributes(p1);
      let insert = '';
      chunk = overlay.chunkSlices0(chunk, p1, p2, (chunk, off, len) => {
        const data = chunk.data;
        if (data) insert += data.slice(off, off + len);
      });
      if (insert) {
        if (insert === QuillConst.EmbedChar && attributes && attributes[QuillConst.EmbedSliceType]) {
          const op: QuillDeltaOpInsert = {insert: attributes[QuillConst.EmbedSliceType] as Record<string, unknown>};
          delete attributes[QuillConst.EmbedSliceType];
          if (!isEmpty(attributes)) op.attributes = attributes;
          ops.push(op);
          continue;
        } else {
          const lastOp = ops[ops.length - 1] as QuillDeltaOpInsert;
          if (lastOp && typeof lastOp.insert === 'string' && deepEqual(lastOp.attributes, attributes)) {
            lastOp.insert += insert;
            continue;
          }
        }
        const op: QuillDeltaOpInsert = {insert};
        if (attributes) op.attributes = attributes;
        ops.push(op);
      }
    }
    this._viewHash = hash;
    this._view = ops;
    return ops;
  }
}