2016-07-19 10:59:31 -07:00
|
|
|
// 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 HOVER_PATH_PATTERN = /^comments\.(left|right)\.\#(\d+)\.__hovering$/;
|
|
|
|
var SPLICE_PATH_PATTERN = /^comments\.(left|right)\.splices$/;
|
|
|
|
|
|
|
|
var RANGE_HIGHLIGHT = 'range';
|
|
|
|
var HOVER_HIGHLIGHT = 'rangeHighlight';
|
|
|
|
|
|
|
|
Polymer({
|
|
|
|
is: 'gr-ranged-comment-layer',
|
|
|
|
|
|
|
|
properties: {
|
|
|
|
comments: Object,
|
|
|
|
_listeners: {
|
|
|
|
type: Array,
|
|
|
|
value: function() { return []; },
|
|
|
|
},
|
|
|
|
_commentMap: {
|
|
|
|
type: Object,
|
|
|
|
value: function() { return {left: [], right: []}; },
|
2016-11-28 15:16:21 -08:00
|
|
|
},
|
2016-07-19 10:59:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
observers: [
|
|
|
|
'_handleCommentChange(comments.*)',
|
|
|
|
],
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Layer method to add annotations to a line.
|
|
|
|
* @param {HTMLElement} el The DIV.contentText element to apply the
|
|
|
|
* annotation to.
|
|
|
|
* @param {GrDiffLine} line The line object.
|
|
|
|
*/
|
2016-07-25 22:46:20 -07:00
|
|
|
annotate: function(el, line) {
|
2016-07-19 10:59:31 -07:00
|
|
|
var 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'));
|
|
|
|
}
|
|
|
|
|
|
|
|
ranges.forEach(function(range) {
|
|
|
|
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: function(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: function(start, end, side) {
|
|
|
|
this._listeners.forEach(function(listener) {
|
|
|
|
listener(start, end, side);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle change in the comments by updating the comment maps and by
|
|
|
|
* emitting appropriate update notifications.
|
|
|
|
* @param {Object} record The change record.
|
|
|
|
*/
|
|
|
|
_handleCommentChange: function(record) {
|
|
|
|
if (!record.path) { return; }
|
|
|
|
|
|
|
|
// If the entire set of comments was changed.
|
|
|
|
if (record.path === 'comments') {
|
|
|
|
this._commentMap.left = this._computeCommentMap(this.comments.left);
|
|
|
|
this._commentMap.right = this._computeCommentMap(this.comments.right);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the change only changed the `hovering` property of a comment.
|
|
|
|
var match = record.path.match(HOVER_PATH_PATTERN);
|
|
|
|
if (match) {
|
|
|
|
var side = match[1];
|
|
|
|
var index = match[2];
|
|
|
|
var comment = this.comments[side][index];
|
|
|
|
if (comment && comment.range) {
|
2016-07-14 12:31:09 -07:00
|
|
|
this._commentMap[side] = this._computeCommentMap(this.comments[side]);
|
2016-07-19 10:59:31 -07:00
|
|
|
this._notifyUpdateRange(
|
|
|
|
comment.range.start_line, comment.range.end_line, side);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If comments were spliced in or out.
|
|
|
|
match = record.path.match(SPLICE_PATH_PATTERN);
|
|
|
|
if (match) {
|
|
|
|
var side = match[1];
|
|
|
|
this._commentMap[side] = this._computeCommentMap(this.comments[side]);
|
|
|
|
this._handleCommentSplice(record.value, side);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Take a list of comments and return a sparse list mapping line numbers to
|
|
|
|
* partial ranges. Uses an end-character-index of -1 to indicate the end of
|
|
|
|
* the line.
|
|
|
|
* @param {Array<Object>} commentList The list of comments.
|
|
|
|
* @return {Object} The sparse list.
|
|
|
|
*/
|
|
|
|
_computeCommentMap: function(commentList) {
|
|
|
|
var result = {};
|
|
|
|
commentList.forEach(function(comment) {
|
|
|
|
if (!comment.range) { return; }
|
|
|
|
var range = comment.range;
|
|
|
|
for (var line = range.start_line; line <= range.end_line; line++) {
|
|
|
|
if (!result[line]) { result[line] = []; }
|
|
|
|
result[line].push({
|
|
|
|
comment: comment,
|
|
|
|
start: line === range.start_line ? range.start_character : 0,
|
|
|
|
end: line === range.end_line ? range.end_character : -1,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Translate a splice record into range update notifications.
|
|
|
|
*/
|
|
|
|
_handleCommentSplice: function(record, side) {
|
|
|
|
if (!record || !record.indexSplices) { return; }
|
|
|
|
record.indexSplices.forEach(function(splice) {
|
|
|
|
var ranges = splice.removed.length ?
|
|
|
|
splice.removed.map(function(c) { return c.range; }) :
|
|
|
|
[splice.object[splice.index].range];
|
|
|
|
ranges.forEach(function(range) {
|
|
|
|
if (!range) { return; }
|
|
|
|
this._notifyUpdateRange(range.start_line, range.end_line, side);
|
|
|
|
}.bind(this));
|
|
|
|
}.bind(this));
|
|
|
|
},
|
|
|
|
|
|
|
|
_getRangesForLine: function(line, side) {
|
|
|
|
var lineNum = side === 'left' ? line.beforeNumber : line.afterNumber;
|
|
|
|
var ranges = this.get(['_commentMap', side, lineNum]) || [];
|
2016-07-14 12:31:09 -07:00
|
|
|
return ranges
|
|
|
|
.map(function(range) {
|
|
|
|
return {
|
|
|
|
start: range.start,
|
|
|
|
end: range.end === -1 ? line.text.length : range.end,
|
|
|
|
hovering: !!range.comment.__hovering,
|
|
|
|
};
|
|
|
|
})
|
|
|
|
.sort(function(a, b) {
|
|
|
|
// Sort the ranges so that hovering highlights are on top.
|
|
|
|
return a.hovering && !b.hovering ? 1 : 0;
|
|
|
|
});
|
2016-07-19 10:59:31 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
})();
|