189 lines
6.7 KiB
JavaScript
189 lines
6.7 KiB
JavaScript
/**
|
|
* @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.
|
|
*/
|
|
(function() {
|
|
'use strict';
|
|
|
|
const HOVER_PATH_PATTERN = /^commentRanges\.\#(\d+)\.hovering$/;
|
|
|
|
const RANGE_HIGHLIGHT = 'range';
|
|
const HOVER_HIGHLIGHT = 'rangeHighlight';
|
|
|
|
const NORMALIZE_RANGE_EVENT = 'normalize-range';
|
|
|
|
/** @typedef {{side: string, range: Gerrit.Range, hovering: boolean}} */
|
|
Gerrit.HoveredRange;
|
|
|
|
Polymer({
|
|
is: 'gr-ranged-comment-layer',
|
|
|
|
properties: {
|
|
/** @type {!Array<!Gerrit.HoveredRange>} */
|
|
commentRanges: Array,
|
|
_listeners: {
|
|
type: Array,
|
|
value() { return []; },
|
|
},
|
|
_rangesMap: {
|
|
type: Object,
|
|
value() { return {left: {}, right: {}}; },
|
|
},
|
|
},
|
|
|
|
observers: [
|
|
'_handleCommentRangesChange(commentRanges.*)',
|
|
],
|
|
|
|
/**
|
|
* Layer method to add annotations to a line.
|
|
* @param {!HTMLElement} el The DIV.contentText element to apply the
|
|
* annotation to.
|
|
* @param {!Object} line The line object. (GrDiffLine)
|
|
*/
|
|
annotate(el, line) {
|
|
let ranges = [];
|
|
if (line.type === GrDiffLine.Type.REMOVE || (
|
|
line.type === GrDiffLine.Type.BOTH &&
|
|
el.getAttribute('data-side') !== 'right')) {
|
|
ranges = ranges.concat(this._getRangesForLine(line, 'left'));
|
|
}
|
|
if (line.type === GrDiffLine.Type.ADD || (
|
|
line.type === GrDiffLine.Type.BOTH &&
|
|
el.getAttribute('data-side') !== 'left')) {
|
|
ranges = ranges.concat(this._getRangesForLine(line, 'right'));
|
|
}
|
|
|
|
for (const range of ranges) {
|
|
GrAnnotation.annotateElement(el, range.start,
|
|
range.end - range.start,
|
|
range.hovering ? HOVER_HIGHLIGHT : RANGE_HIGHLIGHT);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Register a listener for layer updates.
|
|
* @param {function(number, number, string)} fn The update handler function.
|
|
* Should accept as arguments the line numbers for the start and end of
|
|
* the update and the side as a string.
|
|
*/
|
|
addListener(fn) {
|
|
this._listeners.push(fn);
|
|
},
|
|
|
|
/**
|
|
* Notify Layer listeners of changes to annotations.
|
|
* @param {number} start The line where the update starts.
|
|
* @param {number} end The line where the update ends.
|
|
* @param {string} side The side of the update. ('left' or 'right')
|
|
*/
|
|
_notifyUpdateRange(start, end, side) {
|
|
for (const listener of this._listeners) {
|
|
listener(start, end, side);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handle change in the ranges by updating the ranges maps and by
|
|
* emitting appropriate update notifications.
|
|
* @param {Object} record The change record.
|
|
*/
|
|
_handleCommentRangesChange(record) {
|
|
if (!record) return;
|
|
|
|
// If the entire set of comments was changed.
|
|
if (record.path === 'commentRanges') {
|
|
this._rangesMap = {left: {}, right: {}};
|
|
for (const {side, range, hovering} of record.value) {
|
|
this._updateRangesMap(
|
|
side, range, hovering, (forLine, start, end, hovering) => {
|
|
forLine.push({start, end, hovering});
|
|
});
|
|
}
|
|
}
|
|
|
|
// If the change only changed the `hovering` property of a comment.
|
|
const match = record.path.match(HOVER_PATH_PATTERN);
|
|
if (match) {
|
|
const commentRangesIndex = match[1];
|
|
const {side, range, hovering} = this.commentRanges[commentRangesIndex];
|
|
this._updateRangesMap(
|
|
side, range, hovering, (forLine, start, end, hovering) => {
|
|
const index = forLine.findIndex(lineRange =>
|
|
lineRange.start === start && lineRange.end === end);
|
|
forLine[index].hovering = hovering;
|
|
});
|
|
}
|
|
|
|
// If comments were spliced in or out.
|
|
if (record.path === 'commentRanges.splices') {
|
|
for (const indexSplice of record.value.indexSplices) {
|
|
const removed = indexSplice.removed;
|
|
for (const {side, range, hovering} of removed) {
|
|
this._updateRangesMap(
|
|
side, range, hovering, (forLine, start, end) => {
|
|
const index = forLine.findIndex(lineRange =>
|
|
lineRange.start === start && lineRange.end === end);
|
|
forLine.splice(index, 1);
|
|
});
|
|
}
|
|
const added = indexSplice.object.slice(
|
|
indexSplice.index, indexSplice.index + indexSplice.addedCount);
|
|
for (const {side, range, hovering} of added) {
|
|
this._updateRangesMap(
|
|
side, range, hovering, (forLine, start, end, hovering) => {
|
|
forLine.push({start, end, hovering});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_updateRangesMap(side, range, hovering, operation) {
|
|
const forSide = this._rangesMap[side] || (this._rangesMap[side] = {});
|
|
for (let line = range.start_line; line <= range.end_line; line++) {
|
|
const forLine = forSide[line] || (forSide[line] = []);
|
|
const start = line === range.start_line ? range.start_character : 0;
|
|
const end = line === range.end_line ? range.end_character : -1;
|
|
operation(forLine, start, end, hovering);
|
|
}
|
|
this._notifyUpdateRange(range.start_line, range.end_line, side);
|
|
},
|
|
|
|
_getRangesForLine(line, side) {
|
|
const lineNum = side === 'left' ? line.beforeNumber : line.afterNumber;
|
|
const ranges = this.get(['_rangesMap', side, lineNum]) || [];
|
|
return ranges
|
|
.map(range => {
|
|
range.end = range.end === -1 ? line.text.length : range.end;
|
|
|
|
// Normalize invalid ranges where the start is after the end but the
|
|
// start still makes sense. Set the end to the end of the line.
|
|
// @see Issue 5744
|
|
if (range.start >= range.end && range.start < line.text.length) {
|
|
range.end = line.text.length;
|
|
this.$.reporting.reportInteraction(NORMALIZE_RANGE_EVENT,
|
|
'Modified invalid comment range on l.' + lineNum +
|
|
' of the ' + side + ' side');
|
|
}
|
|
|
|
return range;
|
|
})
|
|
// Sort the ranges so that hovering highlights are on top.
|
|
.sort((a, b) => a.hovering && !b.hovering ? 1 : 0);
|
|
},
|
|
});
|
|
})();
|