Commit 2e425d52 authored by Tiger Oakes's avatar Tiger Oakes Committed by Commit Bot

SuperSize: Display percentage of highlighted symbols in pie chart

Builds on the highlighting code to display highlights in the pie chart.
The hightlights appear as bars along the chart, similar to the diff
view.

Bug: 847599
Change-Id: I5dc0f163ebaa5d46b331b084391e6e09645e5dd3
Reviewed-on: https://chromium-review.googlesource.com/1152090Reviewed-by: default avatarPeter Wen <wnwen@chromium.org>
Reviewed-by: default avatarSam Maier <smaier@chromium.org>
Commit-Queue: Tiger Oakes <tigero@google.com>
Cr-Commit-Position: refs/heads/master@{#578441}
parent 14fba836
...@@ -210,19 +210,19 @@ ...@@ -210,19 +210,19 @@
<fieldset id="highlight-container"> <fieldset id="highlight-container">
<legend class="subhead">Highlight symbols</legend> <legend class="subhead">Highlight symbols</legend>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="clearhighlight" name="highlight" value="clear" checked data-dynamic> <input type="radio" id="clearhighlight" name="highlight" value="clear" checked>
<label class="radio-label" for="clearhighlight">None</label> <label class="radio-label" for="clearhighlight">None</label>
</div> </div>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="hothighlight" name="highlight" value="hot" data-dynamic> <input type="radio" id="hothighlight" name="highlight" value="hot">
<label class="radio-label" for="hothighlight">Hot code</label> <label class="radio-label" for="hothighlight">Hot code</label>
</div> </div>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="generatedhighlight" name="highlight" value="generated" data-dynamic> <input type="radio" id="generatedhighlight" name="highlight" value="generated">
<label class="radio-label" for="generatedhighlight">Generated files</label> <label class="radio-label" for="generatedhighlight">Generated files</label>
</div> </div>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="coveragehightlight" name="highlight" value="coverage" data-dynamic> <input type="radio" id="coveragehightlight" name="highlight" value="coverage">
<label class="radio-label" for="coveragehightlight">Code coverage</label> <label class="radio-label" for="coveragehightlight">Code coverage</label>
</div> </div>
</fieldset> </fieldset>
...@@ -346,7 +346,10 @@ ...@@ -346,7 +346,10 @@
<header class="header-info"> <header class="header-info">
<h4 class="subhead size-info"></h4> <h4 class="subhead size-info"></h4>
<p class="body-2 path-info"></p> <p class="body-2 path-info"></p>
<p class="caption type-info type-info-container"></p> <p class="caption type-info-container">
<span class="type-info"></span>
<span class="flags-info"></span>
</p>
</header> </header>
<table class="type-breakdown-info"> <table class="type-breakdown-info">
<thead> <thead>
......
...@@ -40,6 +40,8 @@ const displayInfocard = (() => { ...@@ -40,6 +40,8 @@ const displayInfocard = (() => {
this._iconInfo = this._infocard.querySelector('.icon-info'); this._iconInfo = this._infocard.querySelector('.icon-info');
/** @type {HTMLSpanElement} */ /** @type {HTMLSpanElement} */
this._typeInfo = this._infocard.querySelector('.type-info'); this._typeInfo = this._infocard.querySelector('.type-info');
/** @type {HTMLSpanElement} */
this._flagsInfo = this._infocard.querySelector('.flags-info');
/** /**
* Last symbol type displayed. * Last symbol type displayed.
...@@ -105,6 +107,22 @@ const displayInfocard = (() => { ...@@ -105,6 +107,22 @@ const displayInfocard = (() => {
this._iconInfo.appendChild(icon); this._iconInfo.appendChild(icon);
} }
/**
* Returns a string representing the flags in the node.
* @param {TreeNode} node
*/
_flagsString(node) {
if (!node.flags) {
return '';
}
const flagsString = Array.from(_FLAG_LABELS)
.filter(([flag]) => hasFlag(flag, node))
.map(([, part]) => part)
.join(',');
return `{${flagsString}}`;
}
/** /**
* Toggle wheter or not the card is visible. * Toggle wheter or not the card is visible.
* @param {boolean} isHidden * @param {boolean} isHidden
...@@ -133,6 +151,7 @@ const displayInfocard = (() => { ...@@ -133,6 +151,7 @@ const displayInfocard = (() => {
this._setTypeContent(icon); this._setTypeContent(icon);
this._lastType = type; this._lastType = type;
} }
this._flagsInfo.textContent = this._flagsString(node);
} }
/** /**
...@@ -148,12 +167,6 @@ const displayInfocard = (() => { ...@@ -148,12 +167,6 @@ const displayInfocard = (() => {
} }
class SymbolInfocard extends Infocard { class SymbolInfocard extends Infocard {
constructor(id) {
super(id);
/** @type {HTMLSpanElement} */
this._flagsInfo = this._infocard.querySelector('.flags-info');
}
/** /**
* @param {SVGSVGElement} icon Icon to display * @param {SVGSVGElement} icon Icon to display
*/ */
...@@ -162,31 +175,6 @@ const displayInfocard = (() => { ...@@ -162,31 +175,6 @@ const displayInfocard = (() => {
super._setTypeContent(icon); super._setTypeContent(icon);
this._iconInfo.style.backgroundColor = color; this._iconInfo.style.backgroundColor = color;
} }
/**
* Updates the DOM for the info card.
* @param {TreeNode} node
*/
_updateInfocard(node) {
super._updateInfocard(node);
this._flagsInfo.textContent = this._flagsString(node);
}
/**
* Returns a string representing the flags in the node.
* @param {TreeNode} symbolNode
*/
_flagsString(symbolNode) {
if (!symbolNode.flags) {
return '';
}
const flagsString = Array.from(_FLAG_LABELS)
.filter(([flag]) => hasFlag(flag, symbolNode))
.map(([, part]) => part)
.join(',');
return `{${flagsString}}`;
}
} }
class ContainerInfocard extends Infocard { class ContainerInfocard extends Infocard {
...@@ -233,19 +221,33 @@ const displayInfocard = (() => { ...@@ -233,19 +221,33 @@ const displayInfocard = (() => {
icon.classList.add('canvas-overlay'); icon.classList.add('canvas-overlay');
} }
_flagsString(containerNode) {
const flags = super._flagsString(containerNode);
return flags ? `- contains ${flags}` : '';
}
/** /**
* Draw a slice of a pie chart. * Draw a border around part of a pie chart.
* @param {number} angleStart Starting angle, in radians. * @param {number} angleStart Starting angle, in radians.
* @param {number} percentage Percentage of circle to draw. * @param {number} angleEnd Ending angle, in radians.
* @param {string} fillColor Color of the pie slice.
* @param {string} strokeColor Color of the pie slice border. * @param {string} strokeColor Color of the pie slice border.
* @returns {number} Ending angle, in radians. * @param {number} lineWidth Width of the border.
*/ */
_drawSlice(angleStart, percentage, fillColor, strokeColor) { _drawBorder(angleStart, angleEnd, strokeColor, lineWidth) {
const arcLength = Math.abs(percentage) * 2 * Math.PI; this._ctx.strokeStyle = strokeColor;
const angleEnd = angleStart + arcLength; this._ctx.lineWidth = lineWidth;
if (arcLength === 0) return angleEnd; this._ctx.beginPath();
this._ctx.arc(40, 40, _CANVAS_RADIUS, angleStart, angleEnd);
this._ctx.stroke();
}
/**
* Draw a slice of a pie chart.
* @param {number} angleStart Starting angle, in radians.
* @param {number} angleEnd Ending angle, in radians.
* @param {string} fillColor Color of the pie slice.
*/
_drawSlice(angleStart, angleEnd, fillColor) {
// Update DOM // Update DOM
this._ctx.fillStyle = fillColor; this._ctx.fillStyle = fillColor;
// Move cursor to center, where line will start // Move cursor to center, where line will start
...@@ -256,16 +258,6 @@ const displayInfocard = (() => { ...@@ -256,16 +258,6 @@ const displayInfocard = (() => {
// Move cursor back to center // Move cursor back to center
this._ctx.closePath(); this._ctx.closePath();
this._ctx.fill(); this._ctx.fill();
if (strokeColor) {
this._ctx.strokeStyle = strokeColor;
this._ctx.lineWidth = 16;
this._ctx.beginPath();
this._ctx.arc(40, 40, _CANVAS_RADIUS, angleStart, angleEnd);
this._ctx.stroke();
}
return angleEnd;
} }
/** /**
...@@ -319,6 +311,7 @@ const displayInfocard = (() => { ...@@ -319,6 +311,7 @@ const displayInfocard = (() => {
(a, b) => b[1].size - a[1].size (a, b) => b[1].size - a[1].size
); );
const diffMode = state.has('diff_mode'); const diffMode = state.has('diff_mode');
const highlightMode = state.has('highlight');
let totalSize = 0; let totalSize = 0;
for (const [, stats] of statsEntries) { for (const [, stats] of statsEntries) {
totalSize += Math.abs(stats.size); totalSize += Math.abs(stats.size);
...@@ -331,13 +324,26 @@ const displayInfocard = (() => { ...@@ -331,13 +324,26 @@ const displayInfocard = (() => {
delete extraRows[type]; delete extraRows[type];
const {color} = getIconStyle(type); const {color} = getIconStyle(type);
const percentage = stats.size / totalSize; const percentage = stats.size / totalSize;
let stroke = '';
if (diffMode) {
stroke = stats.size > 0 ? '#ea4335' : '#34a853';
}
angleStart = this._drawSlice(angleStart, percentage, color, stroke);
this._updateBreakdownRow(this._infoRows[type], stats, percentage); this._updateBreakdownRow(this._infoRows[type], stats, percentage);
const arcLength = Math.abs(percentage) * 2 * Math.PI;
if (arcLength > 0) {
const angleEnd = angleStart + arcLength;
this._drawSlice(angleStart, angleEnd, color);
if (highlightMode) {
const highlightPercent = stats.highlight / totalSize;
const highlightArcLength = Math.abs(highlightPercent) * 2 * Math.PI;
const highlightEnd = (angleStart + highlightArcLength);
this._drawBorder(angleStart, highlightEnd, '#feefc3', 32);
}
if (diffMode) {
const strokeColor = stats.size > 0 ? '#ea4335' : '#34a853';
this._drawBorder(angleStart, angleEnd, strokeColor, 16);
}
angleStart = angleEnd;
}
} }
// Hide unused types // Hide unused types
......
...@@ -24,8 +24,16 @@ ...@@ -24,8 +24,16 @@
* @prop {string} type Type of this node. If this node has children, the string * @prop {string} type Type of this node. If this node has children, the string
* may have a second character to denote the most common child. * may have a second character to denote the most common child.
* @prop {number} flags * @prop {number} flags
* @prop {{[type: string]: {size:number,count:number}}} childStats Stats about * @prop {{[type: string]: TreeNodeChildStats}} childStats Stats about this
* this node's descendants, organized by symbol type. * node's descendants, organized by symbol type.
*/
/**
* @typedef {object} TreeNodeChildStats Stats about a node's descendants of
* a certain type.
* @prop {number} size Byte size
* @prop {number} count Number of symbols
* @prop {number} highlight Byte size of children that should be
* highlighted.
*/ */
/** /**
......
...@@ -16,12 +16,6 @@ const newTreeElement = (() => { ...@@ -16,12 +16,6 @@ const newTreeElement = (() => {
const _SPECIAL_CHAR_REGEX = /(::|(?:\.*\/)+|#)/g; const _SPECIAL_CHAR_REGEX = /(::|(?:\.*\/)+|#)/g;
/** Insert zero-width space after capture group */ /** Insert zero-width space after capture group */
const _ZERO_WIDTH_SPACE = '$&\u200b'; const _ZERO_WIDTH_SPACE = '$&\u200b';
/** @type {{[highlight: string]: number}} */
const _HIGHLIGHT_STATE_FLAGS = {
hot: _FLAGS.HOT,
generated: _FLAGS.GENERATED_SOURCE,
coverage: _FLAGS.COVERAGE,
};
// Templates for tree nodes in the UI. // Templates for tree nodes in the UI.
/** @type {HTMLTemplateElement} Template for leaves in the tree */ /** @type {HTMLTemplateElement} Template for leaves in the tree */
...@@ -53,19 +47,17 @@ const newTreeElement = (() => { ...@@ -53,19 +47,17 @@ const newTreeElement = (() => {
* @param {TreeNode} node Data about this symbol name element's tree node. * @param {TreeNode} node Data about this symbol name element's tree node.
*/ */
function _highlightSymbolName(symbolNameElement, node) { function _highlightSymbolName(symbolNameElement, node) {
if (state.has('method_count')) { const dexMethodStats = node.childStats[_DEX_METHOD_SYMBOL_TYPE];
const {count = 0} = node.childStats[_DEX_METHOD_SYMBOL_TYPE] || {}; if (dexMethodStats && dexMethodStats.count < 0) {
if (count < 0) { // This symbol was removed between the before and after versions.
// This symbol was removed between the before and after versions. symbolNameElement.classList.add('removed');
symbolNameElement.classList.add('removed');
}
} }
const flagToCheck = _HIGHLIGHT_STATE_FLAGS[state.get('highlight')]; if (state.has('highlight')) {
if (flagToCheck != null && hasFlag(flagToCheck, node)) { const stats = Object.values(node.childStats);
symbolNameElement.classList.add('highlight') if (stats.some(stat => stat.highlight > 0)) {
} else { symbolNameElement.classList.add('highlight');
symbolNameElement.classList.remove('highlight') }
} }
} }
...@@ -376,13 +368,6 @@ const newTreeElement = (() => { ...@@ -376,13 +368,6 @@ const newTreeElement = (() => {
form.elements form.elements
.namedItem('byteunit') .namedItem('byteunit')
.addEventListener('change', _handleDynamicInputChange('.size', _setSize)); .addEventListener('change', _handleDynamicInputChange('.size', _setSize));
// When the `highlight` state changes, update all .symbol-name elements.
document
.getElementById('highlight-container')
.addEventListener(
'change',
_handleDynamicInputChange('.symbol-name', _highlightSymbolName)
);
_symbolTree.addEventListener('keydown', _handleKeyNavigation); _symbolTree.addEventListener('keydown', _handleKeyNavigation);
_symbolTree.addEventListener('focusin', event => { _symbolTree.addEventListener('focusin', event => {
......
...@@ -38,6 +38,11 @@ importScripts('./shared.js'); ...@@ -38,6 +38,11 @@ importScripts('./shared.js');
const _PATH_SEP = '/'; const _PATH_SEP = '/';
const _DEMO_DATA_URL = 'demo.ndjson'; const _DEMO_DATA_URL = 'demo.ndjson';
const _NAMES_TO_FLAGS = Object.freeze({
hot: _FLAGS.HOT,
generated: _FLAGS.GENERATED_SOURCE,
coverage: _FLAGS.COVERAGE,
});
/** @param {FileEntry} fileEntry */ /** @param {FileEntry} fileEntry */
function getSourcePath(fileEntry) { function getSourcePath(fileEntry) {
...@@ -118,11 +123,14 @@ class TreeBuilder { ...@@ -118,11 +123,14 @@ class TreeBuilder {
* @param {(symbolNode: TreeNode) => boolean} options.filterTest Called to see * @param {(symbolNode: TreeNode) => boolean} options.filterTest Called to see
* if a symbol should be included. If a symbol fails the test, it will not be * if a symbol should be included. If a symbol fails the test, it will not be
* attached to the tree. * attached to the tree.
* @param {(symbolNode: TreeNode) => boolean} options.highlightTest Called to
* see if a symbol should be highlighted.
* @param {string} options.sep Path seperator used to find parent names. * @param {string} options.sep Path seperator used to find parent names.
*/ */
constructor(options) { constructor(options) {
this._getPath = options.getPath; this._getPath = options.getPath;
this._filterTest = options.filterTest; this._filterTest = options.filterTest;
this._highlightTest = options.highlightTest;
this._sep = options.sep || _PATH_SEP; this._sep = options.sep || _PATH_SEP;
this.rootNode = createNode({ this.rootNode = createNode({
...@@ -146,7 +154,7 @@ class TreeBuilder { ...@@ -146,7 +154,7 @@ class TreeBuilder {
* @param {TreeNode} node Child node. * @param {TreeNode} node Child node.
* @param {TreeNode} directParent New parent node. * @param {TreeNode} directParent New parent node.
*/ */
static _attachToParent(node, directParent) { _attachToParent(node, directParent) {
// Link the nodes together // Link the nodes together
directParent.children.push(node); directParent.children.push(node);
node.parent = directParent; node.parent = directParent;
...@@ -158,23 +166,34 @@ class TreeBuilder { ...@@ -158,23 +166,34 @@ class TreeBuilder {
// Update the size and childStats of all ancestors // Update the size and childStats of all ancestors
while (node.parent != null) { while (node.parent != null) {
const {parent} = node; const {parent} = node;
const [containerType, lastBiggestType] = parent.type;
let {size: lastBiggestSize = 0} = // Track the size of `lastBiggestType` for comparisons.
parent.childStats[lastBiggestType] || {}; let [containerType, lastBiggestType] = parent.type;
let lastBiggestSize = 0;
const lastBiggestStats = parent.childStats[lastBiggestType];
if (lastBiggestStats) {
lastBiggestSize = lastBiggestStats.size;
}
for (const [type, stat] of additionalStats) { for (const [type, stat] of additionalStats) {
const parentStat = parent.childStats[type] || {size: 0, count: 0}; let parentStat = parent.childStats[type];
if (parentStat == null) {
parentStat = {size: 0, count: 0, highlight: 0};
parent.childStats[type] = parentStat;
}
parentStat.size += stat.size; parentStat.size += stat.size;
parentStat.count += stat.count; parentStat.count += stat.count;
parent.childStats[type] = parentStat; parentStat.highlight += stat.highlight;
const absSize = Math.abs(parentStat.size); const absSize = Math.abs(parentStat.size);
if (absSize > lastBiggestSize) { if (absSize > lastBiggestSize) {
parent.type = `${containerType}${type}`; lastBiggestType = type;
lastBiggestSize = absSize; lastBiggestSize = absSize;
} }
} }
parent.type = `${containerType}${lastBiggestType}`;
parent.size += additionalSize; parent.size += additionalSize;
parent.flags |= additionalFlags; parent.flags |= additionalFlags;
node = parent; node = parent;
...@@ -186,7 +205,7 @@ class TreeBuilder { ...@@ -186,7 +205,7 @@ class TreeBuilder {
* into containers, based on the class of the dex methods. * into containers, based on the class of the dex methods.
* @param {TreeNode} node * @param {TreeNode} node
*/ */
static _joinDexMethodClasses(node) { _joinDexMethodClasses(node) {
const hasDexMethods = node.childStats[_DEX_METHOD_SYMBOL_TYPE] != null; const hasDexMethods = node.childStats[_DEX_METHOD_SYMBOL_TYPE] != null;
if (!hasDexMethods || node.children == null) return node; if (!hasDexMethods || node.children == null) return node;
...@@ -202,8 +221,8 @@ class TreeBuilder { ...@@ -202,8 +221,8 @@ class TreeBuilder {
const splitIndex = childNode.idPath.lastIndexOf('#'); const splitIndex = childNode.idPath.lastIndexOf('#');
const isDexMethodWithClass = const isDexMethodWithClass =
childNode.type === _DEX_METHOD_SYMBOL_TYPE && childNode.type === _DEX_METHOD_SYMBOL_TYPE &&
splitIndex > childNode.shortNameIndex; splitIndex > childNode.shortNameIndex;
if (isDexMethodWithClass) { if (isDexMethodWithClass) {
// Get the idPath of the class // Get the idPath of the class
...@@ -221,7 +240,7 @@ class TreeBuilder { ...@@ -221,7 +240,7 @@ class TreeBuilder {
// Adjust the dex method's short name so it starts after the "#" // Adjust the dex method's short name so it starts after the "#"
childNode.shortNameIndex = splitIndex + 1; childNode.shortNameIndex = splitIndex + 1;
TreeBuilder._attachToParent(childNode, classNode); this._attachToParent(childNode, classNode);
} else { } else {
otherSymbols.push(childNode); otherSymbols.push(childNode);
} }
...@@ -235,7 +254,9 @@ class TreeBuilder { ...@@ -235,7 +254,9 @@ class TreeBuilder {
node.children.push(containerNode); node.children.push(containerNode);
} }
} else { } else {
node.children.forEach(TreeBuilder._joinDexMethodClasses); for (const child of node.children) {
this._joinDexMethodClasses(child);
}
} }
return node; return node;
} }
...@@ -258,7 +279,7 @@ class TreeBuilder { ...@@ -258,7 +279,7 @@ class TreeBuilder {
* @param {number} depth How many levels of children to keep. * @param {number} depth How many levels of children to keep.
* @returns {TreeNode} * @returns {TreeNode}
*/ */
static formatNode(node, depth = 1) { formatNode(node, depth = 1) {
const childDepth = depth - 1; const childDepth = depth - 1;
// `null` represents that the children have not been loaded yet // `null` represents that the children have not been loaded yet
let children = null; let children = null;
...@@ -269,11 +290,11 @@ class TreeBuilder { ...@@ -269,11 +290,11 @@ class TreeBuilder {
// If there is 1 child, include it so the UI doesn't need to make a // If there is 1 child, include it so the UI doesn't need to make a
// roundtrip in order to expand the chain. // roundtrip in order to expand the chain.
children = node.children children = node.children
.map(n => TreeBuilder.formatNode(n, childDepth)) .map(n => this.formatNode(n, childDepth))
.sort(_compareFunc); .sort(_compareFunc);
} }
return TreeBuilder._joinDexMethodClasses( return this._joinDexMethodClasses(
Object.assign({}, node, { Object.assign({}, node, {
children, children,
parent: null, parent: null,
...@@ -328,7 +349,7 @@ class TreeBuilder { ...@@ -328,7 +349,7 @@ class TreeBuilder {
} }
// attach node to the newly found parent // attach node to the newly found parent
TreeBuilder._attachToParent(childNode, parentNode); this._attachToParent(childNode, parentNode);
return parentNode; return parentNode;
} }
...@@ -352,18 +373,29 @@ class TreeBuilder { ...@@ -352,18 +373,29 @@ class TreeBuilder {
const size = symbol[_KEYS.SIZE]; const size = symbol[_KEYS.SIZE];
const type = symbol[_KEYS.TYPE]; const type = symbol[_KEYS.TYPE];
const count = symbol[_KEYS.COUNT] || 1; const count = symbol[_KEYS.COUNT] || 1;
const flags = symbol[_KEYS.FLAGS] || 0;
const symbolNode = createNode({ const symbolNode = createNode({
// Join file path to symbol name with a ":" // Join file path to symbol name with a ":"
idPath: `${idPath}:${symbol[_KEYS.SYMBOL_NAME]}`, idPath: `${idPath}:${symbol[_KEYS.SYMBOL_NAME]}`,
shortNameIndex: idPath.length + 1, shortNameIndex: idPath.length + 1,
size, size,
type, type,
flags: symbol[_KEYS.FLAGS] || 0, flags,
childStats: {[type]: {size, count}}, childStats: {
[type]: {
size,
count,
highlight: 0,
},
},
}); });
if (this._highlightTest(symbolNode)) {
symbolNode.childStats[type].highlight = size;
}
if (this._filterTest(symbolNode)) { if (this._filterTest(symbolNode)) {
TreeBuilder._attachToParent(symbolNode, fileNode); this._attachToParent(symbolNode, fileNode);
} }
} }
// unless we filtered out every symbol belonging to this file, // unless we filtered out every symbol belonging to this file,
...@@ -564,6 +596,7 @@ function parseOptions(options) { ...@@ -564,6 +596,7 @@ function parseOptions(options) {
const groupBy = params.get('group_by') || 'source_path'; const groupBy = params.get('group_by') || 'source_path';
const methodCountMode = params.has('method_count'); const methodCountMode = params.has('method_count');
const filterGeneratedFiles = params.has('generated_filter'); const filterGeneratedFiles = params.has('generated_filter');
const flagToHighlight = _NAMES_TO_FLAGS[params.get('highlight')];
let minSymbolSize = Number(params.get('min_size')); let minSymbolSize = Number(params.get('min_size'));
if (Number.isNaN(minSymbolSize)) { if (Number.isNaN(minSymbolSize)) {
...@@ -632,7 +665,15 @@ function parseOptions(options) { ...@@ -632,7 +665,15 @@ function parseOptions(options) {
return filters.every(fn => fn(symbolNode)); return filters.every(fn => fn(symbolNode));
} }
return {groupBy, filterTest, url}; /** @type {(symbolNode: TreeNode) => boolean} */
let highlightTest;
if (flagToHighlight) {
highlightTest = symbolNode => hasFlag(flagToHighlight, symbolNode);
} else {
highlightTest = () => false;
}
return {groupBy, filterTest, highlightTest, url};
} }
/** @type {TreeBuilder | null} */ /** @type {TreeBuilder | null} */
...@@ -644,10 +685,12 @@ const fetcher = new DataFetcher('data.ndjson'); ...@@ -644,10 +685,12 @@ const fetcher = new DataFetcher('data.ndjson');
* @param {string} groupBy Sets how the tree is grouped. * @param {string} groupBy Sets how the tree is grouped.
* @param {(symbolNode: TreeNode) => boolean} filterTest Filter function that * @param {(symbolNode: TreeNode) => boolean} filterTest Filter function that
* each symbol is tested against * each symbol is tested against
* @param {(symbolNode: TreeNode) => boolean} highlightTest Filter function that
* each symbol's flags are tested against
* @param {(msg: TreeProgress) => void} onProgress * @param {(msg: TreeProgress) => void} onProgress
* @returns {Promise<TreeProgress>} * @returns {Promise<TreeProgress>}
*/ */
async function buildTree(groupBy, filterTest, onProgress) { async function buildTree(groupBy, filterTest, highlightTest, onProgress) {
/** @type {Meta | null} Object from the first line of the data file */ /** @type {Meta | null} Object from the first line of the data file */
let meta = null; let meta = null;
...@@ -665,6 +708,7 @@ async function buildTree(groupBy, filterTest, onProgress) { ...@@ -665,6 +708,7 @@ async function buildTree(groupBy, filterTest, onProgress) {
sep: groupBy === 'component' ? '>' : _PATH_SEP, sep: groupBy === 'component' ? '>' : _PATH_SEP,
getPath: getPathMap[groupBy], getPath: getPathMap[groupBy],
filterTest, filterTest,
highlightTest,
}); });
/** /**
...@@ -684,7 +728,7 @@ async function buildTree(groupBy, filterTest, onProgress) { ...@@ -684,7 +728,7 @@ async function buildTree(groupBy, filterTest, onProgress) {
} }
const message = { const message = {
root: TreeBuilder.formatNode(data.root || builder.rootNode), root: builder.formatNode(data.root || builder.rootNode),
percent, percent,
diffMode: meta && meta.diff_mode, diffMode: meta && meta.diff_mode,
}; };
...@@ -738,10 +782,10 @@ async function buildTree(groupBy, filterTest, onProgress) { ...@@ -738,10 +782,10 @@ async function buildTree(groupBy, filterTest, onProgress) {
} }
const actions = { const actions = {
/** @param {{input:string|null,options:string}} data */ /** @param {{input:string|null,options:string}} param0 */
load(data) { load({input, options}) {
const {groupBy, filterTest, url} = parseOptions(data.options); const {groupBy, filterTest, highlightTest, url} = parseOptions(options);
if (data.input === 'from-url://') { if (input === 'from-url://') {
if (url) { if (url) {
// Display the data from the `data_url` query parameter // Display the data from the `data_url` query parameter
console.info('Displaying data from', url); console.info('Displaying data from', url);
...@@ -754,12 +798,12 @@ const actions = { ...@@ -754,12 +798,12 @@ const actions = {
// something. // something.
fetcher.setInput(_DEMO_DATA_URL); fetcher.setInput(_DEMO_DATA_URL);
} }
} else if (data.input != null) { } else if (input != null) {
console.info('Displaying uploaded data'); console.info('Displaying uploaded data');
fetcher.setInput(data.input); fetcher.setInput(input);
} }
return buildTree(groupBy, filterTest, progress => { return buildTree(groupBy, filterTest, highlightTest, progress => {
// @ts-ignore // @ts-ignore
self.postMessage(progress); self.postMessage(progress);
}); });
...@@ -768,7 +812,7 @@ const actions = { ...@@ -768,7 +812,7 @@ const actions = {
async open(path) { async open(path) {
if (!builder) throw new Error('Called open before load'); if (!builder) throw new Error('Called open before load');
const node = builder.find(path); const node = builder.find(path);
return TreeBuilder.formatNode(node); return builder.formatNode(node);
}, },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment