Formerly, the annotation layer interface provided the GrAnnotation library as a parameter to the `annotate` method. This was so the layer would not necessarily need to import the library at the module level and instead could use it as a utility toolbox. With this change, the library is no-longer part of the interface and the layers are now expected to import it at the module layer (if they have a use for it). Change-Id: I49b96c67ec724708c2861ab6be3ce27a53cc1b05
		
			
				
	
	
		
			186 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// 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: []}; },
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    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.
 | 
						|
     */
 | 
						|
    annotate: function(el, line) {
 | 
						|
      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) {
 | 
						|
          this._commentMap[side] = this._computeCommentMap(this.comments[side]);
 | 
						|
          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]) || [];
 | 
						|
      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;
 | 
						|
          });
 | 
						|
    },
 | 
						|
  });
 | 
						|
})();
 |