Ignore duplicate syntax layer ranges

HighlightJS has a bug when highlighting md files. It would produce
hundreds of nested `strong` and `emphasis` ranges (for every line).
Large md files (e.g. in https://crrev.com/c/3564486) would then be
incredibly slow to render.

There are a couple of options to work around this bug. It seemed
best to just ignore duplicate ranges in the worker.

The fix was verified by building a local version of the syntax-worker
and loading it using Dev Helper with the change mentioned above.

Release-Notes: skip
Google-Bug-Id: b/227797617
Change-Id: I475fcb175d3fc479b3e8cb3768dfb95a6ce4229e
diff --git a/polygerrit-ui/app/utils/syntax-util.ts b/polygerrit-ui/app/utils/syntax-util.ts
index b9d0597..03774bd 100644
--- a/polygerrit-ui/app/utils/syntax-util.ts
+++ b/polygerrit-ui/app/utils/syntax-util.ts
@@ -38,6 +38,15 @@
     .replace(/&/g, '&');
 }
 
+function equal(r: SyntaxLayerRange) {
+  return (s: SyntaxLayerRange) =>
+    r.start === s.start && r.length === s.length && r.className === s.className;
+}
+
+function unique(r: SyntaxLayerRange, index: number, array: SyntaxLayerRange[]) {
+  return index === array.findIndex(equal(r));
+}
+
 /**
  * HighlightJS produces one long HTML string with HTML elements spanning
  * multiple lines. <gr-diff> is line based, needs all elements closed at the end
@@ -87,7 +96,9 @@
         range.length = lineLength - range.start;
       }
     }
-    rangesPerLine.push({ranges: ranges.filter(r => r.length > 0)});
+    rangesPerLine.push({
+      ranges: ranges.filter(r => r.length > 0).filter(unique),
+    });
   }
   if (carryOverRanges.length > 0) {
     throw new Error('unclosed <span>s in highlighted code');
diff --git a/polygerrit-ui/app/utils/syntax-util_test.ts b/polygerrit-ui/app/utils/syntax-util_test.ts
index 81cdf57..4d381fb 100644
--- a/polygerrit-ui/app/utils/syntax-util_test.ts
+++ b/polygerrit-ui/app/utils/syntax-util_test.ts
@@ -80,6 +80,15 @@
       );
     });
 
+    test('removal of duplicate spans', async () => {
+      assert.deepEqual(
+        highlightedStringToRanges(
+          '<span class="d"><span class="d">asdfqwer</span></span>'
+        ),
+        [{ranges: [{start: 0, length: 8, className: 'd'}]}]
+      );
+    });
+
     test('one line, two spans one after another', async () => {
       assert.deepEqual(
         highlightedStringToRanges(