7f162dbcb6
This change analyzes and links the output of --code-comments in the disassembly view within turbolizer with the other views, such that selecting these comments will also select the respective blocks/lines/nodes within the other views. The block start comments (e.g. -- B4 start --) are linked with the blocks in the schedule phase view and vice versa. The source position comments (e.g. -- primes.js:3:10 --) select the respective spans, lines, and nodes in the JavaScript code view, the schedule phase view, and the other compilation phase views respectively, and vice versa. It also modifies the display of the line and column numbers in the source position comments to be offset from 1 instead of 0 and ignore the initial source position of the first line of code (from removal of the function name in the compiler). Also fixed the bug where previous selections weren't being cleared properly across multiple views, adding appropriate clear calls when using the selection broker. Review-Url: https://codereview.chromium.org/2133663002 Cr-Commit-Position: refs/heads/master@{#37627}
402 lines
11 KiB
JavaScript
402 lines
11 KiB
JavaScript
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
"use strict";
|
|
|
|
class TextView extends View {
|
|
constructor(id, broker, patterns, allowSpanSelection) {
|
|
super(id, broker);
|
|
let view = this;
|
|
view.sortedPositionList = [];
|
|
view.nodePositionMap = [];
|
|
view.positionNodeMap = [];
|
|
view.textListNode = view.divNode.getElementsByTagName('ul')[0];
|
|
view.fillerSvgElement = view.divElement.append("svg").attr('version','1.1').attr("width", "0");
|
|
view.patterns = patterns;
|
|
view.allowSpanSelection = allowSpanSelection;
|
|
view.nodeToLineMap = [];
|
|
var selectionHandler = {
|
|
clear: function() {
|
|
broker.clear(selectionHandler);
|
|
},
|
|
select: function(items, selected) {
|
|
for (let i of items) {
|
|
if (selected) {
|
|
i.classList.add("selected");
|
|
} else {
|
|
i.classList.remove("selected");
|
|
}
|
|
}
|
|
broker.clear(selectionHandler);
|
|
broker.select(selectionHandler, view.getRanges(items), selected);
|
|
},
|
|
selectionDifference: function(span1, inclusive1, span2, inclusive2) {
|
|
return null;
|
|
},
|
|
brokeredSelect: function(ranges, selected) {
|
|
let locations = view.rangesToLocations(ranges);
|
|
view.selectLocations(locations, selected, true);
|
|
},
|
|
brokeredClear: function() {
|
|
view.selection.clear();
|
|
}
|
|
};
|
|
view.selection = new Selection(selectionHandler);
|
|
broker.addSelectionHandler(selectionHandler);
|
|
}
|
|
|
|
setPatterns(patterns) {
|
|
let view = this;
|
|
view.patterns = patterns;
|
|
}
|
|
|
|
clearText() {
|
|
let view = this;
|
|
while (view.textListNode.firstChild) {
|
|
view.textListNode.removeChild(view.textListNode.firstChild);
|
|
}
|
|
}
|
|
|
|
rangeToLocation(range) {
|
|
return range;
|
|
}
|
|
|
|
rangesToLocations(ranges) {
|
|
let view = this;
|
|
let nodes = new Set();
|
|
let result = [];
|
|
for (let range of ranges) {
|
|
let start = range[0];
|
|
let end = range[1];
|
|
let block_id = range[3];
|
|
let location = { pos_start: start, pos_end: end, block_id: block_id };
|
|
if (range[2] !== null && range[2] != -1) {
|
|
location.node_id = range[2];
|
|
if (range[0] == -1 && range[1] == -1) {
|
|
location.pos_start = view.nodePositionMap[location.node_id];
|
|
location.pos_end = location.pos_start + 1;
|
|
}
|
|
} else {
|
|
if (range[0] != undefined) {
|
|
location.pos_start = range[0];
|
|
location.pos_end = range[1];
|
|
}
|
|
}
|
|
result.push(location);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
sameLocation(l1, l2) {
|
|
let view = this;
|
|
if (l1.block_id != undefined && l2.block_id != undefined &&
|
|
l1.block_id == l2.block_id && l1.node_id === undefined) {
|
|
return true;
|
|
}
|
|
|
|
if (l1.address != undefined && l1.address == l2.address) {
|
|
return true;
|
|
}
|
|
|
|
let node1 = l1.node_id;
|
|
let node2 = l2.node_id;
|
|
|
|
if (node1 === undefined && node2 == undefined) {
|
|
if (l1.pos_start === undefined || l2.pos_start == undefined) {
|
|
return false;
|
|
}
|
|
if (l1.pos_start == -1 || l2.pos_start == -1) {
|
|
return false;
|
|
}
|
|
if (l1.pos_start < l2.pos_start) {
|
|
return l1.pos_end > l2.pos_start;
|
|
} {
|
|
return l1.pos_start < l2.pos_end;
|
|
}
|
|
}
|
|
|
|
if (node1 === undefined) {
|
|
let lower = lowerBound(view.positionNodeMap, l1.pos_start, undefined, function(a, b) {
|
|
var node = a[b];
|
|
return view.nodePositionMap[node];
|
|
} );
|
|
while (++lower < view.positionNodeMap.length &&
|
|
view.nodePositionMap[view.positionNodeMap[lower]] < l1.pos_end) {
|
|
if (view.positionNodeMap[lower] == node2) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (node2 === undefined) {
|
|
let lower = lowerBound(view.positionNodeMap, l2.pos_start, undefined, function(a, b) {
|
|
var node = a[b];
|
|
return view.nodePositionMap[node];
|
|
} );
|
|
while (++lower < view.positionNodeMap.length &&
|
|
view.nodePositionMap[view.positionNodeMap[lower]] < l2.pos_end) {
|
|
if (view.positionNodeMap[lower] == node1) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return l1.node_id == l2.node_id;
|
|
}
|
|
|
|
setNodePositionMap(map) {
|
|
let view = this;
|
|
view.nodePositionMap = map;
|
|
view.positionNodeMap = [];
|
|
view.sortedPositionList = [];
|
|
let next = 0;
|
|
for (let i in view.nodePositionMap) {
|
|
view.sortedPositionList[next] = Number(view.nodePositionMap[i]);
|
|
view.positionNodeMap[next++] = i;
|
|
}
|
|
view.sortedPositionList = sortUnique(view.sortedPositionList,
|
|
function(a,b) { return a - b; });
|
|
this.positionNodeMap.sort(function(a,b) {
|
|
let result = view.nodePositionMap[a] - view.nodePositionMap[b];
|
|
if (result != 0) return result;
|
|
return a - b;
|
|
});
|
|
}
|
|
|
|
selectLocations(locations, selected, makeVisible) {
|
|
let view = this;
|
|
for (let l of locations) {
|
|
for (let i = 0; i < view.textListNode.children.length; ++i) {
|
|
let child = view.textListNode.children[i];
|
|
if (child.location != undefined && view.sameLocation(l, child.location)) {
|
|
view.selectCommon(child, selected, makeVisible);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
getRanges(items) {
|
|
let result = [];
|
|
let lastObject = null;
|
|
for (let i of items) {
|
|
if (i.location) {
|
|
let location = i.location;
|
|
let start = -1;
|
|
let end = -1;
|
|
let node_id = -1;
|
|
let block_id = -1;
|
|
if (location.node_id !== undefined) {
|
|
node_id = location.node_id;
|
|
}
|
|
if (location.block_id !== undefined) {
|
|
block_id = location.block_id;
|
|
}
|
|
if (location.pos_start !== undefined) {
|
|
start = location.pos_start;
|
|
end = location.pos_end;
|
|
} else {
|
|
if (this.nodePositionMap && this.nodePositionMap[node_id]) {
|
|
start = this.nodePositionMap[node_id];
|
|
end = start + 1;
|
|
}
|
|
}
|
|
if (lastObject == null ||
|
|
(lastObject[2] != node_id ||
|
|
lastObject[0] != start ||
|
|
lastObject[1] != end ||
|
|
lastObject[3] != block_id)) {
|
|
lastObject = [start, end, node_id, block_id];
|
|
result.push(lastObject);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
createFragment(text, style) {
|
|
let view = this;
|
|
let span = document.createElement("SPAN");
|
|
span.onmousedown = function(e) {
|
|
view.mouseDownSpan(span, e);
|
|
}
|
|
if (style != undefined) {
|
|
span.classList.add(style);
|
|
}
|
|
span.innerText = text;
|
|
return span;
|
|
}
|
|
|
|
appendFragment(li, fragment) {
|
|
li.appendChild(fragment);
|
|
}
|
|
|
|
processLine(line) {
|
|
let view = this;
|
|
let result = [];
|
|
let patternSet = 0;
|
|
while (true) {
|
|
let beforeLine = line;
|
|
for (let pattern of view.patterns[patternSet]) {
|
|
let matches = line.match(pattern[0]);
|
|
if (matches != null) {
|
|
if (matches[0] != '') {
|
|
let style = pattern[1] != null ? pattern[1] : {};
|
|
let text = matches[0];
|
|
if (text != '') {
|
|
let fragment = view.createFragment(matches[0], style.css);
|
|
if (style.link) {
|
|
fragment.classList.add('linkable-text');
|
|
fragment.link = style.link;
|
|
}
|
|
result.push(fragment);
|
|
if (style.location != undefined) {
|
|
let location = style.location(text);
|
|
if (location != undefined) {
|
|
fragment.location = location;
|
|
}
|
|
}
|
|
}
|
|
line = line.substr(matches[0].length);
|
|
}
|
|
let nextPatternSet = patternSet;
|
|
if (pattern.length > 2) {
|
|
nextPatternSet = pattern[2];
|
|
}
|
|
if (line == "") {
|
|
if (nextPatternSet != -1) {
|
|
throw("illegal parsing state in text-view in patternSet" + patternSet);
|
|
}
|
|
return result;
|
|
}
|
|
patternSet = nextPatternSet;
|
|
break;
|
|
}
|
|
}
|
|
if (beforeLine == line) {
|
|
throw("input not consumed in text-view in patternSet" + patternSet);
|
|
}
|
|
}
|
|
}
|
|
|
|
select(s, selected, makeVisible) {
|
|
let view = this;
|
|
view.selection.clear();
|
|
view.selectCommon(s, selected, makeVisible);
|
|
}
|
|
|
|
selectCommon(s, selected, makeVisible) {
|
|
let view = this;
|
|
let firstSelect = makeVisible && view.selection.isEmpty();
|
|
if ((typeof s) === 'function') {
|
|
for (let i = 0; i < view.textListNode.children.length; ++i) {
|
|
let child = view.textListNode.children[i];
|
|
if (child.location && s(child.location)) {
|
|
if (firstSelect) {
|
|
makeContainerPosVisible(view.parentNode, child.offsetTop);
|
|
firstSelect = false;
|
|
}
|
|
view.selection.select(child, selected);
|
|
}
|
|
}
|
|
} else if (s.length) {
|
|
for (let i of s) {
|
|
if (firstSelect) {
|
|
makeContainerPosVisible(view.parentNode, i.offsetTop);
|
|
firstSelect = false;
|
|
}
|
|
view.selection.select(i, selected);
|
|
}
|
|
} else {
|
|
if (firstSelect) {
|
|
makeContainerPosVisible(view.parentNode, s.offsetTop);
|
|
firstSelect = false;
|
|
}
|
|
view.selection.select(s, selected);
|
|
}
|
|
}
|
|
|
|
mouseDownLine(li, e) {
|
|
let view = this;
|
|
e.stopPropagation();
|
|
if (!e.shiftKey) {
|
|
view.selection.clear();
|
|
}
|
|
if (li.location != undefined) {
|
|
view.selectLocations([li.location], true, false);
|
|
}
|
|
}
|
|
|
|
mouseDownSpan(span, e) {
|
|
let view = this;
|
|
if (view.allowSpanSelection) {
|
|
e.stopPropagation();
|
|
if (!e.shiftKey) {
|
|
view.selection.clear();
|
|
}
|
|
select(li, true);
|
|
} else if (span.link) {
|
|
span.link(span.textContent);
|
|
e.stopPropagation();
|
|
}
|
|
}
|
|
|
|
processText(text) {
|
|
let view = this;
|
|
let textLines = text.split(/[\n]/);
|
|
let lineNo = 0;
|
|
for (let line of textLines) {
|
|
let li = document.createElement("LI");
|
|
li.onmousedown = function(e) {
|
|
view.mouseDownLine(li, e);
|
|
}
|
|
li.className = "nolinenums";
|
|
li.lineNo = lineNo++;
|
|
let fragments = view.processLine(line);
|
|
for (let fragment of fragments) {
|
|
view.appendFragment(li, fragment);
|
|
}
|
|
let lineLocation = view.lineLocation(li);
|
|
if (lineLocation != undefined) {
|
|
li.location = lineLocation;
|
|
}
|
|
view.textListNode.appendChild(li);
|
|
}
|
|
}
|
|
|
|
initializeContent(data, rememberedSelection) {
|
|
let view = this;
|
|
view.clearText();
|
|
view.processText(data);
|
|
var fillerSize = document.documentElement.clientHeight -
|
|
view.textListNode.clientHeight;
|
|
if (fillerSize < 0) {
|
|
fillerSize = 0;
|
|
}
|
|
view.fillerSvgElement.attr("height", fillerSize);
|
|
}
|
|
|
|
deleteContent() {
|
|
}
|
|
|
|
isScrollable() {
|
|
return true;
|
|
}
|
|
|
|
detachSelection() {
|
|
return null;
|
|
}
|
|
|
|
lineLocation(li) {
|
|
let view = this;
|
|
for (let i = 0; i < li.children.length; ++i) {
|
|
let fragment = li.children[i];
|
|
if (fragment.location != undefined && !view.allowSpanSelection) {
|
|
return fragment.location;
|
|
}
|
|
}
|
|
}
|
|
}
|