Separates diff processing from diff building Moves the diff-processing functionality of the gr-diff-builder component into a new gr-diff-processor component which exposes a promise-based interface. This is step one of creating an asynchronous (non-blocking) diff rendering system. As much as possible, this change is a transfer of code (with tests) from one component to another, making it easier to verify that functionality has not changed. Cleanup of the code, and refactoring it into a more-testable form will come with later changes. Feature: Issue 3916 Change-Id: I875b03b20bf953b128cbe3c5001ba1f8eba12c61
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js index 1896c76..feb21e2 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js +++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-image.js
@@ -28,9 +28,7 @@ GrDiffBuilderSideBySide.prototype); GrDiffBuilderImage.prototype.constructor = GrDiffBuilderImage; - GrDiffBuilderImage.prototype.emitDiff = function() { - this.emitGroup(this._groups[0]); - + GrDiffBuilderImage.prototype.renderDiffImages = function() { var section = this._createElement('tbody', 'image-diff'); this._emitImagePair(section);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html index 315692a..3f92e44 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html +++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -14,12 +14,14 @@ limitations under the License. --> <link rel="import" href="../../../bower_components/polymer/polymer.html"> +<link rel="import" href="../gr-diff-processor/gr-diff-processor.html"> <dom-module id="gr-diff-builder"> <template> <div class="contentWrapper"> <content></content> </div> + <gr-diff-processor id="processor"></gr-diff-processor> </template> <script src="../gr-diff/gr-diff-line.js"></script> <script src="../gr-diff/gr-diff-group.js"></script> @@ -59,7 +61,11 @@ render: function(diff, comments, prefs) { this._builder = this._getDiffBuilder(diff, comments, prefs); - this._renderDiff(); + + this.$.processor.context = prefs.context; + this.$.processor.keyLocations = this._getCommentLocations(comments); + this.$.processor.process(diff.content) + .then(this._renderDiff.bind(this)); }, getLineElByChild: function(node) { @@ -176,7 +182,7 @@ }, showContext: function(newGroups, sectionEl) { - var groups = this._builder._groups; + var groups = this._builder.groups; // TODO(viktard): Polyfill findIndex for IE10. var contextIndex = groups.findIndex(function(group) { return group.element == sectionEl; @@ -207,9 +213,14 @@ throw Error('Unsupported diff view mode: ' + this.viewMode); }, - _renderDiff: function() { + _renderDiff: function(groups) { + this._builder.groups = groups; + this._clearDiffContent(); this.emitDiff(); + if (this.isImageDiff) { + this._builder.renderDiffImages(); + } this.async(function() { this.fire('render'); }, 1); @@ -218,6 +229,23 @@ _clearDiffContent: function() { this.diffElement.innerHTML = null; }, + + _getCommentLocations: function(comments) { + var result = { + left: {}, + right: {}, + }; + for (var side in comments) { + if (side !== GrDiffBuilder.Side.LEFT && + side !== GrDiffBuilder.Side.RIGHT) { + continue; + } + comments[side].forEach(function(c) { + result[side][c.line || GrDiffLine.FILE] = true; + }); + } + return result; + }, }); })(); </script>
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 b96423e..896b60e 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
@@ -22,10 +22,7 @@ this._comments = comments; this._prefs = prefs; this._outputEl = outputEl; - this._groups = []; - - this._commentLocations = this._getCommentLocations(comments); - this._processContent(diff.content, this._groups, prefs.context); + this.groups = []; } GrDiffBuilder.LESS_THAN_CODE = '<'.charCodeAt(0); @@ -63,8 +60,8 @@ var PARTIAL_CONTEXT_AMOUNT = 10; GrDiffBuilder.prototype.emitDiff = function() { - for (var i = 0; i < this._groups.length; i++) { - this.emitGroup(this._groups[i]); + for (var i = 0; i < this.groups.length; i++) { + this.emitGroup(this.groups[i]); } }; @@ -79,8 +76,8 @@ }; GrDiffBuilder.prototype.renderSection = function(element) { - for (var i = 0; i < this._groups.length; i++) { - var group = this._groups[i]; + for (var i = 0; i < this.groups.length; i++) { + var group = this.groups[i]; if (group.element === element) { var newElement = this.buildSectionElement(group); group.element.parentElement.replaceChild(newElement, group.element); @@ -93,8 +90,8 @@ GrDiffBuilder.prototype.getGroupsByLineRange = function( startLine, endLine, opt_side) { var groups = []; - for (var i = 0; i < this._groups.length; i++) { - var group = this._groups[i]; + for (var i = 0; i < this.groups.length; i++) { + var group = this.groups[i]; if (group.lines.length === 0) { continue; } @@ -139,196 +136,11 @@ function(group) { return group.element; }); }; - GrDiffBuilder.prototype._processContent = function(content, groups, context) { - this._appendFileComments(groups); - - var WHOLE_FILE = -1; - context = content.length > 1 ? context : WHOLE_FILE; - - var lineNums = { - left: 0, - right: 0, - }; - content = this._splitCommonGroupsWithComments(content, lineNums); - for (var i = 0; i < content.length; i++) { - var group = content[i]; - var lines = []; - - if (group[GrDiffBuilder.GroupType.BOTH] !== undefined) { - var rows = group[GrDiffBuilder.GroupType.BOTH]; - this._appendCommonLines(rows, lines, lineNums); - - var hiddenRange = [context, rows.length - context]; - if (i === 0) { - hiddenRange[0] = 0; - } else if (i === content.length - 1) { - hiddenRange[1] = rows.length; - } - - if (context !== WHOLE_FILE && hiddenRange[1] - hiddenRange[0] > 0) { - this._insertContextGroups(groups, lines, hiddenRange); - } else { - groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, lines)); - } - continue; - } - - if (group[GrDiffBuilder.GroupType.REMOVED] !== undefined) { - var highlights = undefined; - if (group[GrDiffBuilder.Highlights.REMOVED] !== undefined) { - highlights = this._normalizeIntralineHighlights( - group[GrDiffBuilder.GroupType.REMOVED], - group[GrDiffBuilder.Highlights.REMOVED]); - } - this._appendRemovedLines(group[GrDiffBuilder.GroupType.REMOVED], lines, - lineNums, highlights); - } - - if (group[GrDiffBuilder.GroupType.ADDED] !== undefined) { - var highlights = undefined; - if (group[GrDiffBuilder.Highlights.ADDED] !== undefined) { - highlights = this._normalizeIntralineHighlights( - group[GrDiffBuilder.GroupType.ADDED], - group[GrDiffBuilder.Highlights.ADDED]); - } - this._appendAddedLines(group[GrDiffBuilder.GroupType.ADDED], lines, - lineNums, highlights); - } - groups.push(new GrDiffGroup(GrDiffGroup.Type.DELTA, lines)); - } - }; - - GrDiffBuilder.prototype._appendFileComments = function(groups) { - var line = new GrDiffLine(GrDiffLine.Type.BOTH); - line.beforeNumber = GrDiffLine.FILE; - line.afterNumber = GrDiffLine.FILE; - groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, [line])); - }; - - GrDiffBuilder.prototype._getCommentLocations = function(comments) { - var result = { - left: {}, - right: {}, - }; - for (var side in comments) { - if (side !== GrDiffBuilder.Side.LEFT && - side !== GrDiffBuilder.Side.RIGHT) { - continue; - } - comments[side].forEach(function(c) { - result[side][c.line || GrDiffLine.FILE] = true; - }); - } - return result; - }; - GrDiffBuilder.prototype._commentIsAtLineNum = function(side, lineNum) { return this._commentLocations[side][lineNum] === true; }; - // In order to show comments out of the bounds of the selected context, - // treat them as separate chunks within the model so that the content (and - // context surrounding it) renders correctly. - GrDiffBuilder.prototype._splitCommonGroupsWithComments = function(content, - lineNums) { - var result = []; - var leftLineNum = lineNums.left; - var rightLineNum = lineNums.right; - for (var i = 0; i < content.length; i++) { - if (!content[i].ab) { - result.push(content[i]); - if (content[i].a) { - leftLineNum += content[i].a.length; - } - if (content[i].b) { - rightLineNum += content[i].b.length; - } - continue; - } - var chunk = content[i].ab; - var currentChunk = {ab: []}; - for (var j = 0; j < chunk.length; j++) { - leftLineNum++; - rightLineNum++; - if (this._commentIsAtLineNum(GrDiffBuilder.Side.LEFT, leftLineNum) || - this._commentIsAtLineNum(GrDiffBuilder.Side.RIGHT, rightLineNum)) { - if (currentChunk.ab && currentChunk.ab.length > 0) { - result.push(currentChunk); - currentChunk = {ab: []}; - } - result.push({ab: [chunk[j]]}); - } else { - currentChunk.ab.push(chunk[j]); - } - } - // != instead of !== because we want to cover both undefined and null. - if (currentChunk.ab != null && currentChunk.ab.length > 0) { - result.push(currentChunk); - } - } - return result; - }; - - // The `highlights` array consists of a list of <skip length, mark length> - // pairs, where the skip length is the number of characters between the - // end of the previous edit and the start of this edit, and the mark - // length is the number of edited characters following the skip. The start - // of the edits is from the beginning of the related diff content lines. - // - // Note that the implied newline character at the end of each line is - // included in the length calculation, and thus it is possible for the - // edits to span newlines. - // - // A line highlight object consists of three fields: - // - contentIndex: The index of the diffChunk `content` field (the line - // being referred to). - // - startIndex: Where the highlight should begin. - // - endIndex: (optional) Where the highlight should end. If omitted, the - // highlight is meant to be a continuation onto the next line. - GrDiffBuilder.prototype._normalizeIntralineHighlights = function(content, - highlights) { - var contentIndex = 0; - var idx = 0; - var normalized = []; - for (var i = 0; i < highlights.length; i++) { - var line = content[contentIndex] + '\n'; - var hl = highlights[i]; - var j = 0; - while (j < hl[0]) { - if (idx === line.length) { - idx = 0; - line = content[++contentIndex] + '\n'; - continue; - } - idx++; - j++; - } - var lineHighlight = { - contentIndex: contentIndex, - startIndex: idx, - }; - - j = 0; - while (line && j < hl[1]) { - if (idx === line.length) { - idx = 0; - line = content[++contentIndex] + '\n'; - normalized.push(lineHighlight); - lineHighlight = { - contentIndex: contentIndex, - startIndex: idx, - }; - continue; - } - idx++; - j++; - } - lineHighlight.endIndex = idx; - normalized.push(lineHighlight); - } - return normalized; - }; - + // TODO (wyatta): Move this completely into the processor. GrDiffBuilder.prototype._insertContextGroups = function(groups, lines, hiddenRange) { var linesBeforeCtx = lines.slice(0, hiddenRange[0]); @@ -350,46 +162,6 @@ } }; - GrDiffBuilder.prototype._appendCommonLines = function(rows, lines, lineNums) { - for (var i = 0; i < rows.length; i++) { - var line = new GrDiffLine(GrDiffLine.Type.BOTH); - line.text = rows[i]; - line.beforeNumber = ++lineNums.left; - line.afterNumber = ++lineNums.right; - lines.push(line); - } - }; - - GrDiffBuilder.prototype._appendRemovedLines = function(rows, lines, lineNums, - opt_highlights) { - for (var i = 0; i < rows.length; i++) { - var line = new GrDiffLine(GrDiffLine.Type.REMOVE); - line.text = rows[i]; - line.beforeNumber = ++lineNums.left; - if (opt_highlights) { - line.highlights = opt_highlights.filter(function(hl) { - return hl.contentIndex === i; - }); - } - lines.push(line); - } - }; - - GrDiffBuilder.prototype._appendAddedLines = function(rows, lines, lineNums, - opt_highlights) { - for (var i = 0; i < rows.length; i++) { - var line = new GrDiffLine(GrDiffLine.Type.ADD); - line.text = rows[i]; - line.afterNumber = ++lineNums.right; - if (opt_highlights) { - line.highlights = opt_highlights.filter(function(hl) { - return hl.contentIndex === i; - }); - } - lines.push(line); - } - }; - GrDiffBuilder.prototype._createContextControl = function(section, line) { if (!line.contextGroup || !line.contextGroup.lines.length) { return null;
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 879d8592..cda4dc8 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
@@ -18,11 +18,23 @@ <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> <title>gr-diff-builder</title> +<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script> <script src="../../../bower_components/web-component-tester/browser.js"></script> <script src="../gr-diff/gr-diff-line.js"></script> <script src="../gr-diff/gr-diff-group.js"></script> <script src="gr-diff-builder.js"></script> +<link rel="import" href="gr-diff-builder.html"> + +<test-fixture id="basic"> + <template> + <gr-diff-builder> + <table id="diffTable"></table> + </gr-diff-builder> + </template> +</test-fixture> + + <script> suite('gr-diff-builder tests', function() { var builder; @@ -36,205 +48,6 @@ builder = new GrDiffBuilder({content: []}, {left: [], right: []}, prefs); }); - test('process loaded content', function() { - var content = [ - { - ab: [ - '<!DOCTYPE html>', - '<meta charset="utf-8">', - ] - }, - { - a: [ - ' Welcome ', - ' to the wooorld of tomorrow!', - ], - b: [ - ' Hello, world!', - ], - }, - { - ab: [ - 'Leela: This is the only place the ship can’t hear us, so ', - 'everyone pretend to shower.', - 'Fry: Same as every day. Got it.', - ] - }, - ]; - var groups = []; - - builder._processContent(content, groups, -1); - - assert.equal(groups.length, 4); - - var group = groups[0]; - assert.equal(group.type, GrDiffGroup.Type.BOTH); - assert.equal(group.lines.length, 1); - assert.equal(group.lines[0].text, ''); - assert.equal(group.lines[0].beforeNumber, GrDiffLine.FILE); - assert.equal(group.lines[0].afterNumber, GrDiffLine.FILE); - - group = groups[1]; - assert.equal(group.type, GrDiffGroup.Type.BOTH); - assert.equal(group.lines.length, 2); - assert.equal(group.lines.length, 2); - - function beforeNumberFn(l) { return l.beforeNumber; } - function afterNumberFn(l) { return l.afterNumber; } - function textFn(l) { return l.text; } - - assert.deepEqual(group.lines.map(beforeNumberFn), [1, 2]); - assert.deepEqual(group.lines.map(afterNumberFn), [1, 2]); - assert.deepEqual(group.lines.map(textFn), [ - '<!DOCTYPE html>', - '<meta charset="utf-8">', - ]); - - group = groups[2]; - assert.equal(group.type, GrDiffGroup.Type.DELTA); - assert.equal(group.lines.length, 3); - assert.equal(group.adds.length, 1); - assert.equal(group.removes.length, 2); - assert.deepEqual(group.removes.map(beforeNumberFn), [3, 4]); - assert.deepEqual(group.adds.map(afterNumberFn), [3]); - assert.deepEqual(group.removes.map(textFn), [ - ' Welcome ', - ' to the wooorld of tomorrow!', - ]); - assert.deepEqual(group.adds.map(textFn), [ - ' Hello, world!', - ]); - - group = groups[3]; - assert.equal(group.type, GrDiffGroup.Type.BOTH); - assert.equal(group.lines.length, 3); - assert.deepEqual(group.lines.map(beforeNumberFn), [5, 6, 7]); - assert.deepEqual(group.lines.map(afterNumberFn), [4, 5, 6]); - assert.deepEqual(group.lines.map(textFn), [ - 'Leela: This is the only place the ship can’t hear us, so ', - 'everyone pretend to shower.', - 'Fry: Same as every day. Got it.', - ]); - }); - - test('insert context groups', function() { - var content = [ - {ab: []}, - {a: ['all work and no play make andybons a dull boy']}, - {ab: []}, - {b: ['elgoog elgoog elgoog']}, - {ab: []}, - ]; - for (var i = 0; i < 100; i++) { - content[0].ab.push('all work and no play make jack a dull boy'); - content[4].ab.push('all work and no play make jill a dull girl'); - } - for (var i = 0; i < 5; i++) { - content[2].ab.push('no tv and no beer make homer go crazy'); - } - var groups = []; - var context = 10; - - builder._processContent(content, groups, context); - - assert.equal(groups[0].type, GrDiffGroup.Type.BOTH); - assert.equal(groups[0].lines.length, 1); - assert.equal(groups[0].lines[0].text, ''); - assert.equal(groups[0].lines[0].beforeNumber, GrDiffLine.FILE); - 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); - groups[1].lines[0].contextGroup.lines.forEach(function(l) { - assert.equal(l.text, content[0].ab[0]); - }); - - assert.equal(groups[2].type, GrDiffGroup.Type.BOTH); - assert.equal(groups[2].lines.length, context); - groups[2].lines.forEach(function(l) { - assert.equal(l.text, content[0].ab[0]); - }); - - assert.equal(groups[3].type, GrDiffGroup.Type.DELTA); - assert.equal(groups[3].lines.length, 1); - assert.equal(groups[3].removes.length, 1); - assert.equal(groups[3].removes[0].text, - 'all work and no play make andybons a dull boy'); - - assert.equal(groups[4].type, GrDiffGroup.Type.BOTH); - assert.equal(groups[4].lines.length, 5); - groups[4].lines.forEach(function(l) { - assert.equal(l.text, content[2].ab[0]); - }); - - assert.equal(groups[5].type, GrDiffGroup.Type.DELTA); - assert.equal(groups[5].lines.length, 1); - assert.equal(groups[5].adds.length, 1); - assert.equal(groups[5].adds[0].text, 'elgoog elgoog elgoog'); - - assert.equal(groups[6].type, GrDiffGroup.Type.BOTH); - assert.equal(groups[6].lines.length, context); - groups[6].lines.forEach(function(l) { - assert.equal(l.text, content[4].ab[0]); - }); - - 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); - groups[7].lines[0].contextGroup.lines.forEach(function(l) { - assert.equal(l.text, content[4].ab[0]); - }); - - content = [ - {a: ['all work and no play make andybons a dull boy']}, - {ab: []}, - {b: ['elgoog elgoog elgoog']}, - ]; - for (var i = 0; i < 50; i++) { - content[1].ab.push('no tv and no beer make homer go crazy'); - } - groups = []; - - builder._processContent(content, groups, 10); - - assert.equal(groups[0].type, GrDiffGroup.Type.BOTH); - assert.equal(groups[0].lines.length, 1); - assert.equal(groups[0].lines[0].text, ''); - assert.equal(groups[0].lines[0].beforeNumber, GrDiffLine.FILE); - assert.equal(groups[0].lines[0].afterNumber, GrDiffLine.FILE); - - assert.equal(groups[1].type, GrDiffGroup.Type.DELTA); - assert.equal(groups[1].lines.length, 1); - assert.equal(groups[1].removes.length, 1); - assert.equal(groups[1].removes[0].text, - 'all work and no play make andybons a dull boy'); - - assert.equal(groups[2].type, GrDiffGroup.Type.BOTH); - assert.equal(groups[2].lines.length, context); - groups[2].lines.forEach(function(l) { - assert.equal(l.text, content[1].ab[0]); - }); - - 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); - groups[3].lines[0].contextGroup.lines.forEach(function(l) { - assert.equal(l.text, content[1].ab[0]); - }); - - assert.equal(groups[4].type, GrDiffGroup.Type.BOTH); - assert.equal(groups[4].lines.length, context); - groups[4].lines.forEach(function(l) { - assert.equal(l.text, content[1].ab[0]); - }); - - assert.equal(groups[5].type, GrDiffGroup.Type.DELTA); - assert.equal(groups[5].lines.length, 1); - assert.equal(groups[5].adds.length, 1); - assert.equal(groups[5].adds[0].text, 'elgoog elgoog elgoog'); - }); - test('context control buttons', function() { var section = {}; var line = {contextGroup: {lines: []}}; @@ -412,153 +225,11 @@ [{id: 'l3', line: 3}, {id: 'r5', line: 5}]); }); - test('break up common diff chunks', function() { - builder._commentLocations = { - left: {1: true}, - right: {10: true}, - }; - var lineNums = { - left: 0, - right: 0, - }; - var content = [ - { - ab: [ - 'Copyright (C) 2015 The Android Open Source Project', - '', - 'Licensed under the Apache License, Version 2.0 (the "License");', - 'you may not use this file except in compliance with the License.', - 'You may obtain a copy of the License at', - '', - 'http://www.apache.org/licenses/LICENSE-2.0', - '', - 'Unless required by applicable law or agreed to in writing, ', - 'software distributed under the License is distributed on an ', - '"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, ', - 'either express or implied. See the License for the specific ', - 'language governing permissions and limitations under the License.', - ] - } - ]; - var result = builder._splitCommonGroupsWithComments(content, lineNums); - assert.deepEqual(result, [ - { - ab: ['Copyright (C) 2015 The Android Open Source Project'], - }, - { - ab: [ - '', - 'Licensed under the Apache License, Version 2.0 (the "License");', - 'you may not use this file except in compliance with the License.', - 'You may obtain a copy of the License at', - '', - 'http://www.apache.org/licenses/LICENSE-2.0', - '', - 'Unless required by applicable law or agreed to in writing, ', - ] - }, - { - ab: ['software distributed under the License is distributed on an '], - }, - { - ab: [ - '"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, ', - 'either express or implied. See the License for the specific ', - 'language governing permissions and limitations under the License.', - ] - } - ]); - }); - - test('intraline normalization', function() { - // The content and highlights are in the format returned by the Gerrit - // REST API. - var content = [ - ' <section class="summary">', - ' <gr-linked-text content="' + - '[[_computeCurrentRevisionMessage(change)]]"></gr-linked-text>', - ' </section>', - ]; - var highlights = [ - [31, 34], [42, 26] - ]; - var results = GrDiffBuilder.prototype._normalizeIntralineHighlights( - content, highlights); - assert.deepEqual(results, [ - { - contentIndex: 0, - startIndex: 31, - }, - { - contentIndex: 1, - startIndex: 0, - endIndex: 33, - }, - { - contentIndex: 1, - startIndex: 75, - }, - { - contentIndex: 2, - startIndex: 0, - endIndex: 6, - } - ]); - - content = [ - ' this._path = value.path;', - '', - ' // When navigating away from the page, there is a ' + - 'possibility that the', - ' // patch number is no longer a part of the URL ' + - '(say when navigating to', - ' // the top-level change info view) and therefore ' + - 'undefined in `params`.', - ' if (!this._patchRange.patchNum) {', - ]; - highlights = [ - [14, 17], - [11, 70], - [12, 67], - [12, 67], - [14, 29], - ]; - results = GrDiffBuilder.prototype._normalizeIntralineHighlights(content, - highlights); - assert.deepEqual(results, [ - { - contentIndex: 0, - startIndex: 14, - endIndex: 31, - }, - { - contentIndex: 2, - startIndex: 8, - endIndex: 78, - }, - { - contentIndex: 3, - startIndex: 11, - endIndex: 78, - }, - { - contentIndex: 4, - startIndex: 11, - endIndex: 78, - }, - { - contentIndex: 5, - startIndex: 12, - endIndex: 41, - } - ]); - }); - suite('rendering', function() { var content; var outputEl; - setup(function() { + setup(function(done) { var prefs = { line_length: 10, show_tabs: true, @@ -577,32 +248,38 @@ ] }, ]; - outputEl = document.createElement('out'); - builder = - new GrDiffBuilder( - {content: content}, {left: [], right: []}, prefs, outputEl); - builder.buildSectionElement = function(group) { - var section = document.createElement('stub'); - section.textContent = group.lines.reduce(function(acc, line) { - return acc + line.text; - }, ''); - return section; - }; - builder.emitDiff(); + element = fixture('basic'); + outputEl = element.queryEffectiveChildren('#diffTable'); + element.addEventListener('render', function() { + done(); + }); + sinon.stub(element, '_getDiffBuilder', function() { + var builder = new GrDiffBuilder( + {content: content}, {left: [], right: []}, prefs, outputEl); + builder.buildSectionElement = function(group) { + var section = document.createElement('stub'); + section.textContent = group.lines.reduce(function(acc, line) { + return acc + line.text; + }, ''); + return section; + }; + return builder; + }); + element.render({ content: content }, {left: [], right: []}, prefs); }); test('renderSection', function() { var section = outputEl.querySelector('stub:nth-of-type(2)'); var prevInnerHTML = section.innerHTML; section.innerHTML = 'wiped'; - builder.renderSection(section); + element._builder.renderSection(section); section = outputEl.querySelector('stub:nth-of-type(2)'); assert.equal(section.innerHTML, prevInnerHTML); }); test('getSectionsByLineRange one line', function() { var section = outputEl.querySelector('stub:nth-of-type(2)'); - var sections = builder.getSectionsByLineRange(1, 1, 'left'); + var sections = element._builder.getSectionsByLineRange(1, 1, 'left'); assert.equal(sections.length, 1); assert.strictEqual(sections[0], section); }); @@ -612,7 +289,7 @@ outputEl.querySelector('stub:nth-of-type(2)'), outputEl.querySelector('stub:nth-of-type(3)'), ]; - var sections = builder.getSectionsByLineRange(1, 2, 'left'); + var sections = element._builder.getSectionsByLineRange(1, 2, 'left'); assert.equal(sections.length, 2); assert.strictEqual(sections[0], section[0]); assert.strictEqual(sections[1], section[1]);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html new file mode 100644 index 0000000..cae8bad --- /dev/null +++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.html
@@ -0,0 +1,23 @@ +<!-- +Copyright (C) 2016 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<link rel="import" href="../../../bower_components/polymer/polymer.html"> + +<dom-module id="gr-diff-processor"> + <script src="../gr-diff/gr-diff-line.js"></script> + <script src="../gr-diff/gr-diff-group.js"></script> + <script src="gr-diff-processor.js"></script> +</dom-module>
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 new file mode 100644 index 0000000..0d787c1 --- /dev/null +++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
@@ -0,0 +1,303 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +(function() { + 'use strict'; + + var WHOLE_FILE = -1; + + var DiffSide = { + LEFT: 'left', + RIGHT: 'right', + }; + + var DiffGroupType = { + ADDED: 'b', + BOTH: 'ab', + REMOVED: 'a', + }; + + var DiffHighlights = { + ADDED: 'edit_b', + REMOVED: 'edit_a', + }; + + Polymer({ + is: 'gr-diff-processor', + + properties: { + + /** + * The amount of context around collapsed groups. + */ + context: Number, + + /** + * The array of groups output by the processor. + */ + groups: { + type: Array, + notify: true, + }, + + /** + * Locations that should not be collapsed, including the locations of + * comments. + */ + keyLocations: { + type: Object, + value: function() { return {left: {}, right: {}}; }, + }, + + _content: Object, + }, + + process: function(content) { + return new Promise(function(resolve) { + var groups = []; + this._processContent(content, groups, this.context); + this.groups = groups; + resolve(groups); + }.bind(this)); + }, + + _processContent: function(content, groups, context) { + this._appendFileComments(groups); + + context = content.length > 1 ? context : WHOLE_FILE; + + var lineNums = { + left: 0, + right: 0, + }; + content = this._splitCommonGroupsWithComments(content, lineNums); + for (var i = 0; i < content.length; i++) { + var group = content[i]; + var lines = []; + + if (group[DiffGroupType.BOTH] !== undefined) { + var rows = group[DiffGroupType.BOTH]; + this._appendCommonLines(rows, lines, lineNums); + + var hiddenRange = [context, rows.length - context]; + if (i === 0) { + hiddenRange[0] = 0; + } else if (i === content.length - 1) { + hiddenRange[1] = rows.length; + } + + if (context !== WHOLE_FILE && hiddenRange[1] - hiddenRange[0] > 0) { + this._insertContextGroups(groups, lines, hiddenRange); + } else { + groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, lines)); + } + continue; + } + + if (group[DiffGroupType.REMOVED] !== undefined) { + var highlights = undefined; + if (group[DiffHighlights.REMOVED] !== undefined) { + highlights = this._normalizeIntralineHighlights( + group[DiffGroupType.REMOVED], + group[DiffHighlights.REMOVED]); + } + this._appendRemovedLines(group[DiffGroupType.REMOVED], lines, + lineNums, highlights); + } + + if (group[DiffGroupType.ADDED] !== undefined) { + var highlights = undefined; + if (group[DiffHighlights.ADDED] !== undefined) { + highlights = this._normalizeIntralineHighlights( + group[DiffGroupType.ADDED], + group[DiffHighlights.ADDED]); + } + this._appendAddedLines(group[DiffGroupType.ADDED], lines, + lineNums, highlights); + } + groups.push(new GrDiffGroup(GrDiffGroup.Type.DELTA, lines)); + } + }, + + _appendFileComments: function(groups) { + var line = new GrDiffLine(GrDiffLine.Type.BOTH); + line.beforeNumber = GrDiffLine.FILE; + line.afterNumber = GrDiffLine.FILE; + groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, [line])); + }, + + /** + * In order to show comments out of the bounds of the selected context, + * treat them as separate chunks within the model so that the content (and + * context surrounding it) renders correctly. + */ + _splitCommonGroupsWithComments: function(content, lineNums) { + var result = []; + var leftLineNum = lineNums.left; + var rightLineNum = lineNums.right; + for (var i = 0; i < content.length; i++) { + if (!content[i].ab) { + result.push(content[i]); + if (content[i].a) { + leftLineNum += content[i].a.length; + } + if (content[i].b) { + rightLineNum += content[i].b.length; + } + continue; + } + var chunk = content[i].ab; + var currentChunk = {ab: []}; + for (var j = 0; j < chunk.length; j++) { + leftLineNum++; + rightLineNum++; + + if (this.keyLocations[DiffSide.LEFT][leftLineNum] || + this.keyLocations[DiffSide.RIGHT][rightLineNum]) { + if (currentChunk.ab && currentChunk.ab.length > 0) { + result.push(currentChunk); + currentChunk = {ab: []}; + } + result.push({ab: [chunk[j]]}); + } else { + currentChunk.ab.push(chunk[j]); + } + } + // != instead of !== because we want to cover both undefined and null. + if (currentChunk.ab != null && currentChunk.ab.length > 0) { + result.push(currentChunk); + } + } + return result; + }, + + _appendCommonLines: function(rows, lines, lineNums) { + for (var i = 0; i < rows.length; i++) { + var line = new GrDiffLine(GrDiffLine.Type.BOTH); + line.text = rows[i]; + line.beforeNumber = ++lineNums.left; + line.afterNumber = ++lineNums.right; + lines.push(line); + } + }, + + _insertContextGroups: function(groups, lines, hiddenRange) { + var linesBeforeCtx = lines.slice(0, hiddenRange[0]); + var hiddenLines = lines.slice(hiddenRange[0], hiddenRange[1]); + var linesAfterCtx = lines.slice(hiddenRange[1]); + + if (linesBeforeCtx.length > 0) { + groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesBeforeCtx)); + } + + var ctxLine = new GrDiffLine(GrDiffLine.Type.CONTEXT_CONTROL); + ctxLine.contextGroup = + new GrDiffGroup(GrDiffGroup.Type.BOTH, hiddenLines); + groups.push(new GrDiffGroup(GrDiffGroup.Type.CONTEXT_CONTROL, + [ctxLine])); + + if (linesAfterCtx.length > 0) { + groups.push(new GrDiffGroup(GrDiffGroup.Type.BOTH, linesAfterCtx)); + } + }, + + /** + * The `highlights` array consists of a list of <skip length, mark length> + * pairs, where the skip length is the number of characters between the + * end of the previous edit and the start of this edit, and the mark + * length is the number of edited characters following the skip. The start + * of the edits is from the beginning of the related diff content lines. + * + * Note that the implied newline character at the end of each line is + * included in the length calculation, and thus it is possible for the + * edits to span newlines. + * + * A line highlight object consists of three fields: + * - contentIndex: The index of the diffChunk `content` field (the line + * being referred to). + * - startIndex: Where the highlight should begin. + * - endIndex: (optional) Where the highlight should end. If omitted, the + * highlight is meant to be a continuation onto the next line. + */ + _normalizeIntralineHighlights: function(content, highlights) { + var contentIndex = 0; + var idx = 0; + var normalized = []; + for (var i = 0; i < highlights.length; i++) { + var line = content[contentIndex] + '\n'; + var hl = highlights[i]; + var j = 0; + while (j < hl[0]) { + if (idx === line.length) { + idx = 0; + line = content[++contentIndex] + '\n'; + continue; + } + idx++; + j++; + } + var lineHighlight = { + contentIndex: contentIndex, + startIndex: idx, + }; + + j = 0; + while (line && j < hl[1]) { + if (idx === line.length) { + idx = 0; + line = content[++contentIndex] + '\n'; + normalized.push(lineHighlight); + lineHighlight = { + contentIndex: contentIndex, + startIndex: idx, + }; + continue; + } + idx++; + j++; + } + lineHighlight.endIndex = idx; + normalized.push(lineHighlight); + } + return normalized; + }, + + _appendRemovedLines: function(rows, lines, lineNums, opt_highlights) { + for (var i = 0; i < rows.length; i++) { + var line = new GrDiffLine(GrDiffLine.Type.REMOVE); + line.text = rows[i]; + line.beforeNumber = ++lineNums.left; + if (opt_highlights) { + line.highlights = opt_highlights.filter(function(hl) { + return hl.contentIndex === i; + }); + } + lines.push(line); + } + }, + + _appendAddedLines: function(rows, lines, lineNums, opt_highlights) { + for (var i = 0; i < rows.length; i++) { + var line = new GrDiffLine(GrDiffLine.Type.ADD); + line.text = rows[i]; + line.afterNumber = ++lineNums.right; + if (opt_highlights) { + line.highlights = opt_highlights.filter(function(hl) { + return hl.contentIndex === i; + }); + } + lines.push(line); + } + }, + }); +})();
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 new file mode 100644 index 0000000..6a24d6a --- /dev/null +++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
@@ -0,0 +1,406 @@ +<!DOCTYPE html> +<!-- +Copyright (C) 2016 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> +<title>gr-diff-processor test</title> + +<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script> +<script src="../../../bower_components/web-component-tester/browser.js"></script> + +<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html"> +<link rel="import" href="gr-diff-processor.html"> + +<test-fixture id="basic"> + <template> + <gr-diff-processor></gr-diff-processor> + </template> +</test-fixture> + +<script> + suite('gr-diff-processor tests', function() { + var element; + + suite('not logged in', function() { + + setup(function() { + element = fixture('basic'); + + element.context = 4; + }); + + test('process loaded content', function(done) { + var content = [ + { + ab: [ + '<!DOCTYPE html>', + '<meta charset="utf-8">', + ] + }, + { + a: [ + ' Welcome ', + ' to the wooorld of tomorrow!', + ], + b: [ + ' Hello, world!', + ], + }, + { + ab: [ + 'Leela: This is the only place the ship can’t hear us, so ', + 'everyone pretend to shower.', + 'Fry: Same as every day. Got it.', + ] + }, + ]; + + element.process(content).then(function() { + var groups = element.groups; + + assert.equal(groups.length, 4); + + var group = groups[0]; + assert.equal(group.type, GrDiffGroup.Type.BOTH); + assert.equal(group.lines.length, 1); + assert.equal(group.lines[0].text, ''); + assert.equal(group.lines[0].beforeNumber, GrDiffLine.FILE); + assert.equal(group.lines[0].afterNumber, GrDiffLine.FILE); + + group = groups[1]; + assert.equal(group.type, GrDiffGroup.Type.BOTH); + assert.equal(group.lines.length, 2); + assert.equal(group.lines.length, 2); + + function beforeNumberFn(l) { return l.beforeNumber; } + function afterNumberFn(l) { return l.afterNumber; } + function textFn(l) { return l.text; } + + assert.deepEqual(group.lines.map(beforeNumberFn), [1, 2]); + assert.deepEqual(group.lines.map(afterNumberFn), [1, 2]); + assert.deepEqual(group.lines.map(textFn), [ + '<!DOCTYPE html>', + '<meta charset="utf-8">', + ]); + + group = groups[2]; + assert.equal(group.type, GrDiffGroup.Type.DELTA); + assert.equal(group.lines.length, 3); + assert.equal(group.adds.length, 1); + assert.equal(group.removes.length, 2); + assert.deepEqual(group.removes.map(beforeNumberFn), [3, 4]); + assert.deepEqual(group.adds.map(afterNumberFn), [3]); + assert.deepEqual(group.removes.map(textFn), [ + ' Welcome ', + ' to the wooorld of tomorrow!', + ]); + assert.deepEqual(group.adds.map(textFn), [ + ' Hello, world!', + ]); + + group = groups[3]; + assert.equal(group.type, GrDiffGroup.Type.BOTH); + assert.equal(group.lines.length, 3); + assert.deepEqual(group.lines.map(beforeNumberFn), [5, 6, 7]); + assert.deepEqual(group.lines.map(afterNumberFn), [4, 5, 6]); + assert.deepEqual(group.lines.map(textFn), [ + 'Leela: This is the only place the ship can’t hear us, so ', + 'everyone pretend to shower.', + 'Fry: Same as every day. Got it.', + ]); + + done(); + }); + }); + + test('insert context groups', function(done) { + var content = [ + {ab: []}, + {a: ['all work and no play make andybons a dull boy']}, + {ab: []}, + {b: ['elgoog elgoog elgoog']}, + {ab: []}, + ]; + for (var i = 0; i < 100; i++) { + content[0].ab.push('all work and no play make jack a dull boy'); + content[4].ab.push('all work and no play make jill a dull girl'); + } + for (var i = 0; i < 5; i++) { + content[2].ab.push('no tv and no beer make homer go crazy'); + } + + var context = 10; + element.context = context; + + element.process(content).then(function() { + var groups = element.groups; + + assert.equal(groups[0].type, GrDiffGroup.Type.BOTH); + assert.equal(groups[0].lines.length, 1); + assert.equal(groups[0].lines[0].text, ''); + assert.equal(groups[0].lines[0].beforeNumber, GrDiffLine.FILE); + 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); + groups[1].lines[0].contextGroup.lines.forEach(function(l) { + assert.equal(l.text, content[0].ab[0]); + }); + + assert.equal(groups[2].type, GrDiffGroup.Type.BOTH); + assert.equal(groups[2].lines.length, context); + groups[2].lines.forEach(function(l) { + assert.equal(l.text, content[0].ab[0]); + }); + + assert.equal(groups[3].type, GrDiffGroup.Type.DELTA); + assert.equal(groups[3].lines.length, 1); + assert.equal(groups[3].removes.length, 1); + assert.equal(groups[3].removes[0].text, + 'all work and no play make andybons a dull boy'); + + assert.equal(groups[4].type, GrDiffGroup.Type.BOTH); + assert.equal(groups[4].lines.length, 5); + groups[4].lines.forEach(function(l) { + assert.equal(l.text, content[2].ab[0]); + }); + + assert.equal(groups[5].type, GrDiffGroup.Type.DELTA); + assert.equal(groups[5].lines.length, 1); + assert.equal(groups[5].adds.length, 1); + assert.equal(groups[5].adds[0].text, 'elgoog elgoog elgoog'); + + assert.equal(groups[6].type, GrDiffGroup.Type.BOTH); + assert.equal(groups[6].lines.length, context); + groups[6].lines.forEach(function(l) { + assert.equal(l.text, content[4].ab[0]); + }); + + 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); + groups[7].lines[0].contextGroup.lines.forEach(function(l) { + assert.equal(l.text, content[4].ab[0]); + }); + + done(); + }); + }); + + test('insert context groups', function(done) { + content = [ + {a: ['all work and no play make andybons a dull boy']}, + {ab: []}, + {b: ['elgoog elgoog elgoog']}, + ]; + for (var i = 0; i < 50; i++) { + content[1].ab.push('no tv and no beer make homer go crazy'); + } + + var context = 10; + element.context = context; + + element.process(content).then(function() { + var groups = element.groups; + + assert.equal(groups[0].type, GrDiffGroup.Type.BOTH); + assert.equal(groups[0].lines.length, 1); + assert.equal(groups[0].lines[0].text, ''); + assert.equal(groups[0].lines[0].beforeNumber, GrDiffLine.FILE); + assert.equal(groups[0].lines[0].afterNumber, GrDiffLine.FILE); + + assert.equal(groups[1].type, GrDiffGroup.Type.DELTA); + assert.equal(groups[1].lines.length, 1); + assert.equal(groups[1].removes.length, 1); + assert.equal(groups[1].removes[0].text, + 'all work and no play make andybons a dull boy'); + + assert.equal(groups[2].type, GrDiffGroup.Type.BOTH); + assert.equal(groups[2].lines.length, context); + groups[2].lines.forEach(function(l) { + assert.equal(l.text, content[1].ab[0]); + }); + + 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); + groups[3].lines[0].contextGroup.lines.forEach(function(l) { + assert.equal(l.text, content[1].ab[0]); + }); + + assert.equal(groups[4].type, GrDiffGroup.Type.BOTH); + assert.equal(groups[4].lines.length, context); + groups[4].lines.forEach(function(l) { + assert.equal(l.text, content[1].ab[0]); + }); + + assert.equal(groups[5].type, GrDiffGroup.Type.DELTA); + assert.equal(groups[5].lines.length, 1); + assert.equal(groups[5].adds.length, 1); + assert.equal(groups[5].adds[0].text, 'elgoog elgoog elgoog'); + + done(); + }); + }); + + test('break up common diff chunks', function() { + element.keyLocations = { + left: {1: true}, + right: {10: true}, + }; + var lineNums = {left: 0, right: 0}; + + var content = [ + { + ab: [ + 'Copyright (C) 2015 The Android Open Source Project', + '', + 'Licensed under the Apache License, Version 2.0 (the "License");', + 'you may not use this file except in compliance with the ' + + 'License.', + 'You may obtain a copy of the License at', + '', + 'http://www.apache.org/licenses/LICENSE-2.0', + '', + 'Unless required by applicable law or agreed to in writing, ', + 'software distributed under the License is distributed on an ', + '"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, ', + 'either express or implied. See the License for the specific ', + 'language governing permissions and limitations under the ' + + 'License.', + ] + } + ]; + var result = element._splitCommonGroupsWithComments(content, lineNums); + assert.deepEqual(result, [ + { + ab: ['Copyright (C) 2015 The Android Open Source Project'], + }, + { + ab: [ + '', + 'Licensed under the Apache License, Version 2.0 (the "License");', + 'you may not use this file except in compliance with the ' + + 'License.', + 'You may obtain a copy of the License at', + '', + 'http://www.apache.org/licenses/LICENSE-2.0', + '', + 'Unless required by applicable law or agreed to in writing, ', + ] + }, + { + ab: [ + 'software distributed under the License is distributed on an '], + }, + { + ab: [ + '"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, ', + 'either express or implied. See the License for the specific ', + 'language governing permissions and limitations under the ' + + 'License.', + ] + } + ]); + }); + + test('intraline normalization', function() { + // The content and highlights are in the format returned by the Gerrit + // REST API. + var content = [ + ' <section class="summary">', + ' <gr-linked-text content="' + + '[[_computeCurrentRevisionMessage(change)]]"></gr-linked-text>', + ' </section>', + ]; + var highlights = [ + [31, 34], [42, 26] + ]; + + var results = element._normalizeIntralineHighlights(content, + highlights); + assert.deepEqual(results, [ + { + contentIndex: 0, + startIndex: 31, + }, + { + contentIndex: 1, + startIndex: 0, + endIndex: 33, + }, + { + contentIndex: 1, + startIndex: 75, + }, + { + contentIndex: 2, + startIndex: 0, + endIndex: 6, + } + ]); + + content = [ + ' this._path = value.path;', + '', + ' // When navigating away from the page, there is a ' + + 'possibility that the', + ' // patch number is no longer a part of the URL ' + + '(say when navigating to', + ' // the top-level change info view) and therefore ' + + 'undefined in `params`.', + ' if (!this._patchRange.patchNum) {', + ]; + highlights = [ + [14, 17], + [11, 70], + [12, 67], + [12, 67], + [14, 29], + ]; + results = element._normalizeIntralineHighlights(content, highlights); + assert.deepEqual(results, [ + { + contentIndex: 0, + startIndex: 14, + endIndex: 31, + }, + { + contentIndex: 2, + startIndex: 8, + endIndex: 78, + }, + { + contentIndex: 3, + startIndex: 11, + endIndex: 78, + }, + { + contentIndex: 4, + startIndex: 11, + endIndex: 78, + }, + { + contentIndex: 5, + startIndex: 12, + endIndex: 41, + } + ]); + }); + }); + }); +</script>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index 96b97cc..98f7eef 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html
@@ -49,6 +49,7 @@ 'diff/gr-diff-cursor/gr-diff-cursor_test.html', 'diff/gr-diff-highlight/gr-diff-highlight_test.html', 'diff/gr-diff-preferences/gr-diff-preferences_test.html', + 'diff/gr-diff-processor/gr-diff-processor_test.html', 'diff/gr-diff-selection/gr-diff-selection_test.html', 'diff/gr-diff-view/gr-diff-view_test.html', 'diff/gr-diff/gr-diff-group_test.html',