Support multiple groups in CONTEXT_CONTROL lines

We need this because groups with `common: true` should be treated as
"unchanged" and thus hidden in context control blocks, but they are
still DELTA lines that need to be in their own group, separate from
the BOTH lines that are actually completely unchanged.

This change does not yet update the context splitting logic to actually
create such multi-group context lines, it's just a step in that
direction.

Bug: Issue 1062
Change-Id: I32d7fbac42047ff09b4ca261359d3232a8eaa78e
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
index c8d91df..fb9b726 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
@@ -243,8 +243,8 @@
     }
 
     const ctxLine = new GrDiffLine(GrDiffLine.Type.CONTEXT_CONTROL);
-    ctxLine.contextGroup =
-        new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines);
+    ctxLine.contextGroups =
+        [new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines)];
     groups.push(new GrDiffGroup(GrDiffGroup.Type.CONTEXT_CONTROL,
         [ctxLine]));
 
@@ -254,13 +254,15 @@
   };
 
   GrDiffBuilder.prototype._createContextControl = function(section, line) {
-    if (!line.contextGroup || !line.contextGroup.lines.length) {
-      return null;
-    }
+    if (!line.contextGroups) return null;
+
+    const numLines = line.contextGroups.reduce(
+        (sum, contextGroup) => sum + contextGroup.lines.length, 0);
+
+    if (numLines === 0) return null;
 
     const td = this._createElement('td');
-    const showPartialLinks =
-        line.contextGroup.lines.length > PARTIAL_CONTEXT_AMOUNT;
+    const showPartialLinks = numLines > PARTIAL_CONTEXT_AMOUNT;
 
     if (showPartialLinks) {
       td.appendChild(this._createContextButton(
@@ -281,7 +283,7 @@
   };
 
   GrDiffBuilder.prototype._createContextButton = function(type, section, line) {
-    const contextLines = line.contextGroup.lines;
+    const contextLines = line.contextGroups[0].lines;
     const context = PARTIAL_CONTEXT_AMOUNT;
 
     const button = this._createElement('gr-button', 'showContext');
@@ -294,7 +296,7 @@
     if (type === GrDiffBuilder.ContextButtonType.ALL) {
       text = 'Show ' + contextLines.length + ' common line';
       if (contextLines.length > 1) { text += 's'; }
-      groups.push(line.contextGroup);
+      groups.push(...line.contextGroups);
     } else if (type === GrDiffBuilder.ContextButtonType.ABOVE) {
       text = '+' + context + '↑';
       this._insertContextGroups(groups, contextLines,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
index 481db57..fa54756 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
@@ -89,11 +89,11 @@
 
     test('context control buttons', () => {
       const section = {};
-      const line = {contextGroup: {lines: []}};
+      const line = {contextGroups: [{lines: []}]};
 
       // Create 10 lines.
       for (let i = 0; i < 10; i++) {
-        line.contextGroup.lines.push('lorem upsum');
+        line.contextGroups[0].lines.push('lorem upsum');
       }
 
       // Does not include +10 buttons when there are fewer than 11 lines.
@@ -104,7 +104,7 @@
       assert.equal(Polymer.dom(buttons[0]).textContent, 'Show 10 common lines');
 
       // Add another line.
-      line.contextGroup.lines.push('lorem upsum');
+      line.contextGroups[0].lines.push('lorem upsum');
 
       // Includes +10 buttons when there are at least 11 lines.
       td = builder._createContextControl(section, line);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
index 32a81ed..800e9b3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
@@ -337,8 +337,8 @@
         }
 
         const ctxLine = new GrDiffLine(GrDiffLine.Type.CONTEXT_CONTROL);
-        ctxLine.contextGroup =
-            new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines);
+        ctxLine.contextGroups =
+            [new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines)];
         result.push(new GrDiffGroup(GrDiffGroup.Type.CONTEXT_CONTROL,
             [ctxLine]));
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
index 86f70c8..0e57dbf 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
@@ -173,9 +173,9 @@
           assert.equal(groups[0].lines[0].afterNumber, GrDiffLine.FILE);
 
           assert.equal(groups[1].type, GrDiffGroup.Type.CONTEXT_CONTROL);
-          assert.instanceOf(groups[1].lines[0].contextGroup, GrDiffGroup);
-          assert.equal(groups[1].lines[0].contextGroup.lines.length, 90);
-          for (const l of groups[1].lines[0].contextGroup.lines) {
+          assert.instanceOf(groups[1].lines[0].contextGroups[0], GrDiffGroup);
+          assert.equal(groups[1].lines[0].contextGroups[0].lines.length, 90);
+          for (const l of groups[1].lines[0].contextGroups[0].lines) {
             assert.equal(l.text, content[0].ab[0]);
           }
 
@@ -209,9 +209,9 @@
           }
 
           assert.equal(groups[7].type, GrDiffGroup.Type.CONTEXT_CONTROL);
-          assert.instanceOf(groups[7].lines[0].contextGroup, GrDiffGroup);
-          assert.equal(groups[7].lines[0].contextGroup.lines.length, 90);
-          for (const l of groups[7].lines[0].contextGroup.lines) {
+          assert.instanceOf(groups[7].lines[0].contextGroups[0], GrDiffGroup);
+          assert.equal(groups[7].lines[0].contextGroups[0].lines.length, 90);
+          for (const l of groups[7].lines[0].contextGroups[0].lines) {
             assert.equal(l.text, content[4].ab[0]);
           }
 
@@ -254,9 +254,9 @@
           }
 
           assert.equal(groups[3].type, GrDiffGroup.Type.CONTEXT_CONTROL);
-          assert.instanceOf(groups[3].lines[0].contextGroup, GrDiffGroup);
-          assert.equal(groups[3].lines[0].contextGroup.lines.length, 30);
-          for (const l of groups[3].lines[0].contextGroup.lines) {
+          assert.instanceOf(groups[3].lines[0].contextGroups[0], GrDiffGroup);
+          assert.equal(groups[3].lines[0].contextGroups[0].lines.length, 30);
+          for (const l of groups[3].lines[0].contextGroups[0].lines) {
             assert.equal(l.text, content[1].ab[0]);
           }
 
@@ -530,7 +530,7 @@
           assert.equal(result.groups[2].lines.length, element.context);
 
           // The collapsed group has the hidden lines as its context group.
-          assert.equal(result.groups[1].lines[0].contextGroup.lines.length,
+          assert.equal(result.groups[1].lines[0].contextGroups[0].lines.length,
               expectedCollapseSize);
         });
 
@@ -551,7 +551,7 @@
           assert.equal(result.groups[1].lines.length, element.context);
 
           // The collapsed group has the hidden lines as its context group.
-          assert.equal(result.groups[0].lines[0].contextGroup.lines.length,
+          assert.equal(result.groups[0].lines[0].contextGroups[0].lines.length,
               expectedCollapseSize);
         });
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
index 44bb52a..00b1023 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
@@ -31,7 +31,8 @@
   /** @type {number|string} */
   GrDiffLine.prototype.beforeNumber = 0;
 
-  GrDiffLine.prototype.contextGroup = null;
+  /** @type {?Array<Object>} ?Array<!GrDiffLine> */
+  GrDiffLine.prototype.contextGroups = null;
 
   GrDiffLine.prototype.text = '';