function mergeStreams()

in src/helper/merge-html-plugin.ts [73:126]


  function mergeStreams (original: Event[], highlighted: Event[], value: string): string {
    let processed = 0;
    let result = '';
    const nodeStack = [];

    function selectStream (): Event[] {
      if ((original.length === 0) || (highlighted.length === 0)) {
        return (original.length > 0) ? original : highlighted;
      }
      if (original[0].offset !== highlighted[0].offset) {
        return (original[0].offset < highlighted[0].offset) ? original : highlighted;
      }

      return highlighted[0].event === 'start' ? original : highlighted;
    }

    function open (node: Node): void {
      function attributeString (attr: Attr): string {
        return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
      }
      // @ts-expect-error
      result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>';
    }

    function close (node: Node): void {
      result += '</' + tag(node) + '>';
    }

    function render (event: Event): void {
      (event.event === 'start' ? open : close)(event.node);
    }

    while ((original.length > 0) || (highlighted.length > 0)) {
      let stream = selectStream();
      result += escapeHTML(value.substring(processed, stream[0].offset));
      processed = stream[0].offset;
      if (stream === original) {
        nodeStack.reverse().forEach(close);
        do {
          render(stream.splice(0, 1)[0]);
          stream = selectStream();
        } while (stream === original && (stream.length > 0) && stream[0].offset === processed);
        nodeStack.reverse().forEach(open);
      } else {
        if (stream[0].event === 'start') {
          nodeStack.push(stream[0].node);
        } else {
          nodeStack.pop();
        }
        render(stream.splice(0, 1)[0]);
      }
    }
    return result + escapeHTML(value.substr(processed));
  }