Merge "Converting to class and renaming gr-diff-builder element"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.html
new file mode 100644
index 0000000..e8e60c4
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.html
@@ -0,0 +1,54 @@
+<!--
+@license
+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">
+<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
+<link rel="import" href="../gr-coverage-layer/gr-coverage-layer.html">
+<link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
+<link rel="import" href="../../../elements/shared/gr-hovercard/gr-hovercard.html">
+<link rel="import" href="../gr-ranged-comment-layer/gr-ranged-comment-layer.html">
+
+<dom-module id="gr-diff-builder">
+ <template>
+ <div class="contentWrapper">
+ <slot></slot>
+ </div>
+ <gr-ranged-comment-layer
+ id="rangeLayer"
+ comment-ranges="[[commentRanges]]"></gr-ranged-comment-layer>
+ <gr-coverage-layer
+ id="coverageLayerLeft"
+ coverage-ranges="[[_leftCoverageRanges]]"
+ side="left"></gr-coverage-layer>
+ <gr-coverage-layer
+ id="coverageLayerRight"
+ coverage-ranges="[[_rightCoverageRanges]]"
+ side="right"></gr-coverage-layer>
+ <gr-diff-processor
+ id="processor"
+ groups="{{_groups}}"></gr-diff-processor>
+ </template>
+ <script src="../../../scripts/util.js"></script>
+ <script src="../gr-diff/gr-diff-line.js"></script>
+ <script src="../gr-diff/gr-diff-group.js"></script>
+ <script src="../gr-diff-highlight/gr-annotation.js"></script>
+ <script src="gr-diff-builder.js"></script>
+ <script src="gr-diff-builder-side-by-side.js"></script>
+ <script src="gr-diff-builder-unified.js"></script>
+ <script src="gr-diff-builder-image.js"></script>
+ <script src="gr-diff-builder-binary.js"></script>
+ <script src="gr-diff-builder-element.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
new file mode 100644
index 0000000..a6382c4
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.js
@@ -0,0 +1,389 @@
+// Copyright (C) 2020 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';
+
+ const DiffViewMode = {
+ SIDE_BY_SIDE: 'SIDE_BY_SIDE',
+ UNIFIED: 'UNIFIED_DIFF',
+ };
+
+ const TRAILING_WHITESPACE_PATTERN = /\s+$/;
+
+ /**
+ * @appliesMixin Gerrit.FireMixin
+ */
+ class GrDiffBuilderElement extends Polymer.mixinBehaviors( [
+ Gerrit.FireBehavior,
+ ], Polymer.GestureEventListeners(
+ Polymer.LegacyElementMixin(
+ Polymer.Element))) {
+ static get is() { return 'gr-diff-builder'; }
+ /**
+ * Fired when the diff begins rendering.
+ *
+ * @event render-start
+ */
+
+ /**
+ * Fired when the diff finishes rendering text content.
+ *
+ * @event render-content
+ */
+
+ static get properties() {
+ return {
+ diff: Object,
+ changeNum: String,
+ patchNum: String,
+ viewMode: String,
+ isImageDiff: Boolean,
+ baseImage: Object,
+ revisionImage: Object,
+ parentIndex: Number,
+ path: String,
+ projectName: String,
+
+ _builder: Object,
+ _groups: Array,
+ _layers: Array,
+ _showTabs: Boolean,
+ /** @type {!Array<!Gerrit.HoveredRange>} */
+ commentRanges: {
+ type: Array,
+ value: () => [],
+ },
+ /** @type {!Array<!Gerrit.CoverageRange>} */
+ coverageRanges: {
+ type: Array,
+ value: () => [],
+ },
+ _leftCoverageRanges: {
+ type: Array,
+ computed: '_computeLeftCoverageRanges(coverageRanges)',
+ },
+ _rightCoverageRanges: {
+ type: Array,
+ computed: '_computeRightCoverageRanges(coverageRanges)',
+ },
+ /**
+ * The promise last returned from `render()` while the asynchronous
+ * rendering is running - `null` otherwise. Provides a `cancel()`
+ * method that rejects it with `{isCancelled: true}`.
+ *
+ * @type {?Object}
+ */
+ _cancelableRenderPromise: Object,
+ layers: {
+ type: Array,
+ value: [],
+ },
+ };
+ }
+
+ get diffElement() {
+ return this.queryEffectiveChildren('#diffTable');
+ }
+
+ static get observers() {
+ return [
+ '_groupsChanged(_groups.splices)',
+ ];
+ }
+
+ _computeLeftCoverageRanges(coverageRanges) {
+ return coverageRanges.filter(range => range && range.side === 'left');
+ }
+
+ _computeRightCoverageRanges(coverageRanges) {
+ return coverageRanges.filter(range => range && range.side === 'right');
+ }
+
+ render(keyLocations, prefs) {
+ // Setting up annotation layers must happen after plugins are
+ // installed, and |render| satisfies the requirement, however,
+ // |attached| doesn't because in the diff view page, the element is
+ // attached before plugins are installed.
+ this._setupAnnotationLayers();
+
+ this._showTabs = !!prefs.show_tabs;
+ this._showTrailingWhitespace = !!prefs.show_whitespace_errors;
+
+ // Stop the processor if it's running.
+ this.cancel();
+
+ this._builder = this._getDiffBuilder(this.diff, prefs);
+
+ this.$.processor.context = prefs.context;
+ this.$.processor.keyLocations = keyLocations;
+
+ this._clearDiffContent();
+ this._builder.addColumns(this.diffElement, prefs.font_size);
+
+ const isBinary = !!(this.isImageDiff || this.diff.binary);
+
+ this.dispatchEvent(new CustomEvent(
+ 'render-start', {bubbles: true, composed: true}));
+ this._cancelableRenderPromise = util.makeCancelable(
+ this.$.processor.process(this.diff.content, isBinary)
+ .then(() => {
+ if (this.isImageDiff) {
+ this._builder.renderDiff();
+ }
+ this.dispatchEvent(new CustomEvent('render-content',
+ {bubbles: true, composed: true}));
+ }));
+ return this._cancelableRenderPromise
+ .finally(() => { this._cancelableRenderPromise = null; })
+ // Mocca testing does not like uncaught rejections, so we catch
+ // the cancels which are expected and should not throw errors in
+ // tests.
+ .catch(e => { if (!e.isCanceled) return Promise.reject(e); });
+ }
+
+ _setupAnnotationLayers() {
+ const layers = [
+ this._createTrailingWhitespaceLayer(),
+ this._createIntralineLayer(),
+ this._createTabIndicatorLayer(),
+ this.$.rangeLayer,
+ this.$.coverageLayerLeft,
+ this.$.coverageLayerRight,
+ ];
+
+ if (this.layers) {
+ layers.push(...this.layers);
+ }
+ this._layers = layers;
+ }
+
+ getLineElByChild(node) {
+ while (node) {
+ if (node instanceof Element) {
+ if (node.classList.contains('lineNum')) {
+ return node;
+ }
+ if (node.classList.contains('section')) {
+ return null;
+ }
+ }
+ node = node.previousSibling || node.parentElement;
+ }
+ return null;
+ }
+
+ getLineNumberByChild(node) {
+ const lineEl = this.getLineElByChild(node);
+ return lineEl ?
+ parseInt(lineEl.getAttribute('data-value'), 10) :
+ null;
+ }
+
+ getContentByLine(lineNumber, opt_side, opt_root) {
+ return this._builder.getContentByLine(lineNumber, opt_side, opt_root);
+ }
+
+ getContentByLineEl(lineEl) {
+ const root = Polymer.dom(lineEl.parentElement);
+ const side = this.getSideByLineEl(lineEl);
+ const line = lineEl.getAttribute('data-value');
+ return this.getContentByLine(line, side, root);
+ }
+
+ getLineElByNumber(lineNumber, opt_side) {
+ const sideSelector = opt_side ? ('.' + opt_side) : '';
+ return this.diffElement.querySelector(
+ '.lineNum[data-value="' + lineNumber + '"]' + sideSelector);
+ }
+
+ getContentsByLineRange(startLine, endLine, opt_side) {
+ const result = [];
+ this._builder.findLinesByRange(startLine, endLine, opt_side, null,
+ result);
+ return result;
+ }
+
+ getSideByLineEl(lineEl) {
+ return lineEl.classList.contains(GrDiffBuilder.Side.RIGHT) ?
+ GrDiffBuilder.Side.RIGHT : GrDiffBuilder.Side.LEFT;
+ }
+
+ emitGroup(group, sectionEl) {
+ this._builder.emitGroup(group, sectionEl);
+ }
+
+ showContext(newGroups, sectionEl) {
+ const groups = this._builder.groups;
+
+ const contextIndex = groups.findIndex(group =>
+ group.element === sectionEl
+ );
+ groups.splice(contextIndex, 1, ...newGroups);
+
+ for (const newGroup of newGroups) {
+ this._builder.emitGroup(newGroup, sectionEl);
+ }
+ sectionEl.parentNode.removeChild(sectionEl);
+
+ this.async(() => this.fire('render-content'), 1);
+ }
+
+ cancel() {
+ this.$.processor.cancel();
+ if (this._cancelableRenderPromise) {
+ this._cancelableRenderPromise.cancel();
+ this._cancelableRenderPromise = null;
+ }
+ }
+
+ _handlePreferenceError(pref) {
+ const message = `The value of the '${pref}' user preference is ` +
+ `invalid. Fix in diff preferences`;
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message,
+ }, bubbles: true, composed: true}));
+ throw Error(`Invalid preference value: ${pref}`);
+ }
+
+ _getDiffBuilder(diff, prefs) {
+ if (isNaN(prefs.tab_size) || prefs.tab_size <= 0) {
+ this._handlePreferenceError('tab size');
+ return;
+ }
+
+ if (isNaN(prefs.line_length) || prefs.line_length <= 0) {
+ this._handlePreferenceError('diff width');
+ return;
+ }
+
+ let builder = null;
+ if (this.isImageDiff) {
+ builder = new GrDiffBuilderImage(diff, prefs, this.diffElement,
+ this.baseImage, this.revisionImage);
+ } else if (diff.binary) {
+ // If the diff is binary, but not an image.
+ return new GrDiffBuilderBinary(diff, prefs, this.diffElement);
+ } else if (this.viewMode === DiffViewMode.SIDE_BY_SIDE) {
+ builder = new GrDiffBuilderSideBySide(diff, prefs, this.diffElement,
+ this._layers);
+ } else if (this.viewMode === DiffViewMode.UNIFIED) {
+ builder = new GrDiffBuilderUnified(diff, prefs, this.diffElement,
+ this._layers);
+ }
+ if (!builder) {
+ throw Error('Unsupported diff view mode: ' + this.viewMode);
+ }
+ return builder;
+ }
+
+ _clearDiffContent() {
+ this.diffElement.innerHTML = null;
+ }
+
+ _groupsChanged(changeRecord) {
+ if (!changeRecord) { return; }
+ for (const splice of changeRecord.indexSplices) {
+ let group;
+ for (let i = 0; i < splice.addedCount; i++) {
+ group = splice.object[splice.index + i];
+ this._builder.groups.push(group);
+ this._builder.emitGroup(group);
+ }
+ }
+ }
+
+ _createIntralineLayer() {
+ return {
+ // Take a DIV.contentText element and a line object with intraline
+ // differences to highlight and apply them to the element as
+ // annotations.
+ annotate(contentEl, lineNumberEl, line) {
+ const HL_CLASS = 'style-scope gr-diff intraline';
+ for (const highlight of line.highlights) {
+ // The start and end indices could be the same if a highlight is
+ // meant to start at the end of a line and continue onto the
+ // next one. Ignore it.
+ if (highlight.startIndex === highlight.endIndex) { continue; }
+
+ // If endIndex isn't present, continue to the end of the line.
+ const endIndex = highlight.endIndex === undefined ?
+ line.text.length :
+ highlight.endIndex;
+
+ GrAnnotation.annotateElement(
+ contentEl,
+ highlight.startIndex,
+ endIndex - highlight.startIndex,
+ HL_CLASS);
+ }
+ },
+ };
+ }
+
+ _createTabIndicatorLayer() {
+ const show = () => this._showTabs;
+ return {
+ annotate(contentEl, lineNumberEl, line) {
+ // If visible tabs are disabled, do nothing.
+ if (!show()) { return; }
+
+ // Find and annotate the locations of tabs.
+ const split = line.text.split('\t');
+ if (!split) { return; }
+ for (let i = 0, pos = 0; i < split.length - 1; i++) {
+ // Skip forward by the length of the content
+ pos += split[i].length;
+
+ GrAnnotation.annotateElement(contentEl, pos, 1,
+ 'style-scope gr-diff tab-indicator');
+
+ // Skip forward by one tab character.
+ pos++;
+ }
+ },
+ };
+ }
+
+ _createTrailingWhitespaceLayer() {
+ const show = function() {
+ return this._showTrailingWhitespace;
+ }.bind(this);
+
+ return {
+ annotate(contentEl, lineNumberEl, line) {
+ if (!show()) { return; }
+
+ const match = line.text.match(TRAILING_WHITESPACE_PATTERN);
+ if (match) {
+ // Normalize string positions in case there is unicode before or
+ // within the match.
+ const index = GrAnnotation.getStringLength(
+ line.text.substr(0, match.index));
+ const length = GrAnnotation.getStringLength(match[0]);
+ GrAnnotation.annotateElement(contentEl, index, length,
+ 'style-scope gr-diff trailing-whitespace');
+ }
+ },
+ };
+ }
+
+ setBlame(blame) {
+ if (!this._builder || !blame) { return; }
+ this._builder.setBlame(blame);
+ }
+ }
+
+ customElements.define(GrDiffBuilderElement.is, GrDiffBuilderElement);
+})();
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-element_test.html
similarity index 99%
rename from polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
rename to polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.html
index 838ac68..6a9d903 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-element_test.html
@@ -33,7 +33,7 @@
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
-<link rel="import" href="gr-diff-builder.html">
+<link rel="import" href="gr-diff-builder-element.html">
<script>void(0);</script>
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
deleted file mode 100644
index 021d962..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ /dev/null
@@ -1,423 +0,0 @@
-<!--
-@license
-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">
-<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
-<link rel="import" href="../gr-coverage-layer/gr-coverage-layer.html">
-<link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
-<link rel="import" href="../../../elements/shared/gr-hovercard/gr-hovercard.html">
-<link rel="import" href="../gr-ranged-comment-layer/gr-ranged-comment-layer.html">
-
-<dom-module id="gr-diff-builder">
- <template>
- <div class="contentWrapper">
- <slot></slot>
- </div>
- <gr-ranged-comment-layer
- id="rangeLayer"
- comment-ranges="[[commentRanges]]"></gr-ranged-comment-layer>
- <gr-coverage-layer
- id="coverageLayerLeft"
- coverage-ranges="[[_leftCoverageRanges]]"
- side="left"></gr-coverage-layer>
- <gr-coverage-layer
- id="coverageLayerRight"
- coverage-ranges="[[_rightCoverageRanges]]"
- side="right"></gr-coverage-layer>
- <gr-diff-processor
- id="processor"
- groups="{{_groups}}"></gr-diff-processor>
- </template>
- <script src="../../../scripts/util.js"></script>
- <script src="../gr-diff/gr-diff-line.js"></script>
- <script src="../gr-diff/gr-diff-group.js"></script>
- <script src="../gr-diff-highlight/gr-annotation.js"></script>
- <script src="gr-diff-builder.js"></script>
- <script src="gr-diff-builder-side-by-side.js"></script>
- <script src="gr-diff-builder-unified.js"></script>
- <script src="gr-diff-builder-image.js"></script>
- <script src="gr-diff-builder-binary.js"></script>
- <script>
- (function() {
- 'use strict';
-
- const DiffViewMode = {
- SIDE_BY_SIDE: 'SIDE_BY_SIDE',
- UNIFIED: 'UNIFIED_DIFF',
- };
-
- const TRAILING_WHITESPACE_PATTERN = /\s+$/;
-
- Polymer({
- is: 'gr-diff-builder',
-
- /**
- * Fired when the diff begins rendering.
- *
- * @event render-start
- */
-
- /**
- * Fired when the diff finishes rendering text content.
- *
- * @event render-content
- */
-
- properties: {
- diff: Object,
- changeNum: String,
- patchNum: String,
- viewMode: String,
- isImageDiff: Boolean,
- baseImage: Object,
- revisionImage: Object,
- parentIndex: Number,
- path: String,
- projectName: String,
-
- _builder: Object,
- _groups: Array,
- _layers: Array,
- _showTabs: Boolean,
- /** @type {!Array<!Gerrit.HoveredRange>} */
- commentRanges: {
- type: Array,
- value: () => [],
- },
- /** @type {!Array<!Gerrit.CoverageRange>} */
- coverageRanges: {
- type: Array,
- value: () => [],
- },
- _leftCoverageRanges: {
- type: Array,
- computed: '_computeLeftCoverageRanges(coverageRanges)',
- },
- _rightCoverageRanges: {
- type: Array,
- computed: '_computeRightCoverageRanges(coverageRanges)',
- },
- /**
- * The promise last returned from `render()` while the asynchronous
- * rendering is running - `null` otherwise. Provides a `cancel()`
- * method that rejects it with `{isCancelled: true}`.
- *
- * @type {?Object}
- */
- _cancelableRenderPromise: Object,
- layers: {
- type: Array,
- value: [],
- },
- },
-
- behaviors: [
- Gerrit.FireBehavior,
- ],
-
- get diffElement() {
- return this.queryEffectiveChildren('#diffTable');
- },
-
- observers: [
- '_groupsChanged(_groups.splices)',
- ],
-
- _computeLeftCoverageRanges(coverageRanges) {
- return coverageRanges.filter(range => range && range.side === 'left');
- },
-
- _computeRightCoverageRanges(coverageRanges) {
- return coverageRanges.filter(range => range && range.side === 'right');
- },
-
- render(keyLocations, prefs) {
- // Setting up annotation layers must happen after plugins are
- // installed, and |render| satisfies the requirement, however,
- // |attached| doesn't because in the diff view page, the element is
- // attached before plugins are installed.
- this._setupAnnotationLayers();
-
- this._showTabs = !!prefs.show_tabs;
- this._showTrailingWhitespace = !!prefs.show_whitespace_errors;
-
- // Stop the processor if it's running.
- this.cancel();
-
- this._builder = this._getDiffBuilder(this.diff, prefs);
-
- this.$.processor.context = prefs.context;
- this.$.processor.keyLocations = keyLocations;
-
- this._clearDiffContent();
- this._builder.addColumns(this.diffElement, prefs.font_size);
-
- const isBinary = !!(this.isImageDiff || this.diff.binary);
-
- this.dispatchEvent(new CustomEvent(
- 'render-start', {bubbles: true, composed: true}));
- this._cancelableRenderPromise = util.makeCancelable(
- this.$.processor.process(this.diff.content, isBinary)
- .then(() => {
- if (this.isImageDiff) {
- this._builder.renderDiff();
- }
- this.dispatchEvent(new CustomEvent('render-content',
- {bubbles: true, composed: true}));
- }));
- return this._cancelableRenderPromise
- .finally(() => { this._cancelableRenderPromise = null; })
- // Mocca testing does not like uncaught rejections, so we catch
- // the cancels which are expected and should not throw errors in
- // tests.
- .catch(e => { if (!e.isCanceled) return Promise.reject(e); });
- },
-
- _setupAnnotationLayers() {
- const layers = [
- this._createTrailingWhitespaceLayer(),
- this._createIntralineLayer(),
- this._createTabIndicatorLayer(),
- this.$.rangeLayer,
- this.$.coverageLayerLeft,
- this.$.coverageLayerRight,
- ];
-
- if (this.layers) {
- layers.push(...this.layers);
- }
- this._layers = layers;
- },
-
- getLineElByChild(node) {
- while (node) {
- if (node instanceof Element) {
- if (node.classList.contains('lineNum')) {
- return node;
- }
- if (node.classList.contains('section')) {
- return null;
- }
- }
- node = node.previousSibling || node.parentElement;
- }
- return null;
- },
-
- getLineNumberByChild(node) {
- const lineEl = this.getLineElByChild(node);
- return lineEl ?
- parseInt(lineEl.getAttribute('data-value'), 10) :
- null;
- },
-
- getContentByLine(lineNumber, opt_side, opt_root) {
- return this._builder.getContentByLine(lineNumber, opt_side, opt_root);
- },
-
- getContentByLineEl(lineEl) {
- const root = Polymer.dom(lineEl.parentElement);
- const side = this.getSideByLineEl(lineEl);
- const line = lineEl.getAttribute('data-value');
- return this.getContentByLine(line, side, root);
- },
-
- getLineElByNumber(lineNumber, opt_side) {
- const sideSelector = opt_side ? ('.' + opt_side) : '';
- return this.diffElement.querySelector(
- '.lineNum[data-value="' + lineNumber + '"]' + sideSelector);
- },
-
- getContentsByLineRange(startLine, endLine, opt_side) {
- const result = [];
- this._builder.findLinesByRange(startLine, endLine, opt_side, null,
- result);
- return result;
- },
-
- getSideByLineEl(lineEl) {
- return lineEl.classList.contains(GrDiffBuilder.Side.RIGHT) ?
- GrDiffBuilder.Side.RIGHT : GrDiffBuilder.Side.LEFT;
- },
-
- emitGroup(group, sectionEl) {
- this._builder.emitGroup(group, sectionEl);
- },
-
- showContext(newGroups, sectionEl) {
- const groups = this._builder.groups;
-
- const contextIndex = groups.findIndex(group =>
- group.element === sectionEl
- );
- groups.splice(contextIndex, 1, ...newGroups);
-
- for (const newGroup of newGroups) {
- this._builder.emitGroup(newGroup, sectionEl);
- }
- sectionEl.parentNode.removeChild(sectionEl);
-
- this.async(() => this.fire('render-content'), 1);
- },
-
- cancel() {
- this.$.processor.cancel();
- if (this._cancelableRenderPromise) {
- this._cancelableRenderPromise.cancel();
- this._cancelableRenderPromise = null;
- }
- },
-
- _handlePreferenceError(pref) {
- const message = `The value of the '${pref}' user preference is ` +
- `invalid. Fix in diff preferences`;
- this.dispatchEvent(new CustomEvent('show-alert', {
- detail: {
- message,
- }, bubbles: true, composed: true}));
- throw Error(`Invalid preference value: ${pref}`);
- },
-
- _getDiffBuilder(diff, prefs) {
- if (isNaN(prefs.tab_size) || prefs.tab_size <= 0) {
- this._handlePreferenceError('tab size');
- return;
- }
-
- if (isNaN(prefs.line_length) || prefs.line_length <= 0) {
- this._handlePreferenceError('diff width');
- return;
- }
-
- let builder = null;
- if (this.isImageDiff) {
- builder = new GrDiffBuilderImage(diff, prefs, this.diffElement,
- this.baseImage, this.revisionImage);
- } else if (diff.binary) {
- // If the diff is binary, but not an image.
- return new GrDiffBuilderBinary(diff, prefs, this.diffElement);
- } else if (this.viewMode === DiffViewMode.SIDE_BY_SIDE) {
- builder = new GrDiffBuilderSideBySide(diff, prefs, this.diffElement,
- this._layers);
- } else if (this.viewMode === DiffViewMode.UNIFIED) {
- builder = new GrDiffBuilderUnified(diff, prefs, this.diffElement,
- this._layers);
- }
- if (!builder) {
- throw Error('Unsupported diff view mode: ' + this.viewMode);
- }
- return builder;
- },
-
- _clearDiffContent() {
- this.diffElement.innerHTML = null;
- },
-
- _groupsChanged(changeRecord) {
- if (!changeRecord) { return; }
- for (const splice of changeRecord.indexSplices) {
- let group;
- for (let i = 0; i < splice.addedCount; i++) {
- group = splice.object[splice.index + i];
- this._builder.groups.push(group);
- this._builder.emitGroup(group);
- }
- }
- },
-
- _createIntralineLayer() {
- return {
- // Take a DIV.contentText element and a line object with intraline
- // differences to highlight and apply them to the element as
- // annotations.
- annotate(contentEl, lineNumberEl, line) {
- const HL_CLASS = 'style-scope gr-diff intraline';
- for (const highlight of line.highlights) {
- // The start and end indices could be the same if a highlight is
- // meant to start at the end of a line and continue onto the
- // next one. Ignore it.
- if (highlight.startIndex === highlight.endIndex) { continue; }
-
- // If endIndex isn't present, continue to the end of the line.
- const endIndex = highlight.endIndex === undefined ?
- line.text.length :
- highlight.endIndex;
-
- GrAnnotation.annotateElement(
- contentEl,
- highlight.startIndex,
- endIndex - highlight.startIndex,
- HL_CLASS);
- }
- },
- };
- },
-
- _createTabIndicatorLayer() {
- const show = () => this._showTabs;
- return {
- annotate(contentEl, lineNumberEl, line) {
- // If visible tabs are disabled, do nothing.
- if (!show()) { return; }
-
- // Find and annotate the locations of tabs.
- const split = line.text.split('\t');
- if (!split) { return; }
- for (let i = 0, pos = 0; i < split.length - 1; i++) {
- // Skip forward by the length of the content
- pos += split[i].length;
-
- GrAnnotation.annotateElement(contentEl, pos, 1,
- 'style-scope gr-diff tab-indicator');
-
- // Skip forward by one tab character.
- pos++;
- }
- },
- };
- },
-
- _createTrailingWhitespaceLayer() {
- const show = function() {
- return this._showTrailingWhitespace;
- }.bind(this);
-
- return {
- annotate(contentEl, lineNumberEl, line) {
- if (!show()) { return; }
-
- const match = line.text.match(TRAILING_WHITESPACE_PATTERN);
- if (match) {
- // Normalize string positions in case there is unicode before or
- // within the match.
- const index = GrAnnotation.getStringLength(
- line.text.substr(0, match.index));
- const length = GrAnnotation.getStringLength(match[0]);
- GrAnnotation.annotateElement(contentEl, index, length,
- 'style-scope gr-diff trailing-whitespace');
- }
- },
- };
- },
-
- setBlame(blame) {
- if (!this._builder || !blame) { return; }
- this._builder.setBlame(blame);
- },
- });
- })();
- </script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 590537d..003be05 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -20,7 +20,7 @@
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
-<link rel="import" href="../gr-diff-builder/gr-diff-builder.html">
+<link rel="import" href="../gr-diff-builder/gr-diff-builder-element.html">
<link rel="import" href="../gr-diff-highlight/gr-diff-highlight.html">
<link rel="import" href="../gr-diff-selection/gr-diff-selection.html">
<link rel="import" href="../gr-syntax-themes/gr-syntax-theme.html">
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 7decb61..38dde97 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -112,7 +112,7 @@
'core/gr-smart-search/gr-smart-search_test.html',
'diff/gr-comment-api/gr-comment-api_test.html',
'diff/gr-coverage-layer/gr-coverage-layer_test.html',
- 'diff/gr-diff-builder/gr-diff-builder_test.html',
+ 'diff/gr-diff-builder/gr-diff-builder-element_test.html',
'diff/gr-diff-builder/gr-diff-builder-unified_test.html',
'diff/gr-diff-cursor/gr-diff-cursor_test.html',
'diff/gr-diff-highlight/gr-annotation_test.html',