mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 05:35:05 +08:00
Move and rename files. NOAUTOREVERT=true NOPRESUBMIT=true NOTREECHECKS=true Bug: 768828 TBR=darin@chromium.org NOTRY=true Change-Id: I66d3b155808bc5bdbf237b80208e1e552bcf7f28 Reviewed-on: https://chromium-review.googlesource.com/1001153 Reviewed-by: Blink Reformat <blink-reformat@chromium.org> Commit-Queue: Blink Reformat <blink-reformat@chromium.org> Cr-Original-Commit-Position: refs/heads/master@{#549061} Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src Cr-Mirrored-Commit: 0aee4434a4dba42a42abaea9bfbc0cd196a63bc1
305 lines
11 KiB
JavaScript
305 lines
11 KiB
JavaScript
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
/**
|
|
* @typedef {!{
|
|
* id: string,
|
|
* contentProvider: !Common.ContentProvider,
|
|
* line: number,
|
|
* column: number
|
|
* }}
|
|
*/
|
|
Coverage.RawLocation;
|
|
|
|
Coverage.CoverageDecorationManager = class {
|
|
/**
|
|
* @param {!Coverage.CoverageModel} coverageModel
|
|
*/
|
|
constructor(coverageModel) {
|
|
this._coverageModel = coverageModel;
|
|
/** @type {!Map<!Common.ContentProvider, ?TextUtils.Text>} */
|
|
this._textByProvider = new Map();
|
|
/** @type {!Multimap<!Common.ContentProvider, !Workspace.UISourceCode>} */
|
|
this._uiSourceCodeByContentProvider = new Multimap();
|
|
// TODO(crbug.com/720162): get rid of this, use bindings.
|
|
/** @type {!WeakMap<!Workspace.UISourceCode, !Array<!SDK.CSSStyleSheetHeader>>} */
|
|
this._documentUISouceCodeToStylesheets = new WeakMap();
|
|
|
|
for (const uiSourceCode of Workspace.workspace.uiSourceCodes())
|
|
uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this);
|
|
Workspace.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, this._onUISourceCodeAdded, this);
|
|
}
|
|
|
|
reset() {
|
|
for (const uiSourceCode of Workspace.workspace.uiSourceCodes())
|
|
uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._decoratorType);
|
|
}
|
|
|
|
dispose() {
|
|
this.reset();
|
|
Workspace.workspace.removeEventListener(
|
|
Workspace.Workspace.Events.UISourceCodeAdded, this._onUISourceCodeAdded, this);
|
|
}
|
|
|
|
/**
|
|
* @param {!Array<!Coverage.CoverageInfo>} updatedEntries
|
|
*/
|
|
update(updatedEntries) {
|
|
for (const entry of updatedEntries) {
|
|
for (const uiSourceCode of this._uiSourceCodeByContentProvider.get(entry.contentProvider())) {
|
|
uiSourceCode.removeDecorationsForType(Coverage.CoverageDecorationManager._decoratorType);
|
|
uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @return {!Promise<!Array<boolean>>}
|
|
*/
|
|
async usageByLine(uiSourceCode) {
|
|
const result = [];
|
|
const sourceText = new TextUtils.Text(uiSourceCode.content() || '');
|
|
await this._updateTexts(uiSourceCode, sourceText);
|
|
const lineEndings = sourceText.lineEndings();
|
|
for (let line = 0; line < sourceText.lineCount(); ++line) {
|
|
const lineLength = lineEndings[line] - (line ? lineEndings[line - 1] : 0) - 1;
|
|
if (!lineLength) {
|
|
result.push(undefined);
|
|
continue;
|
|
}
|
|
const startLocations = this._rawLocationsForSourceLocation(uiSourceCode, line, 0);
|
|
const endLocations = this._rawLocationsForSourceLocation(uiSourceCode, line, lineLength);
|
|
let used = undefined;
|
|
for (let startIndex = 0, endIndex = 0; startIndex < startLocations.length; ++startIndex) {
|
|
const start = startLocations[startIndex];
|
|
while (endIndex < endLocations.length &&
|
|
Coverage.CoverageDecorationManager._compareLocations(start, endLocations[endIndex]) >= 0)
|
|
++endIndex;
|
|
if (endIndex >= endLocations.length || endLocations[endIndex].id !== start.id)
|
|
continue;
|
|
const end = endLocations[endIndex++];
|
|
const text = this._textByProvider.get(end.contentProvider);
|
|
if (!text)
|
|
continue;
|
|
const textValue = text.value();
|
|
let startOffset = Math.min(text.offsetFromPosition(start.line, start.column), textValue.length - 1);
|
|
let endOffset = Math.min(text.offsetFromPosition(end.line, end.column), textValue.length - 1);
|
|
while (startOffset <= endOffset && /\s/.test(textValue[startOffset]))
|
|
++startOffset;
|
|
while (startOffset <= endOffset && /\s/.test(textValue[endOffset]))
|
|
--endOffset;
|
|
if (startOffset <= endOffset)
|
|
used = this._coverageModel.usageForRange(end.contentProvider, startOffset, endOffset);
|
|
if (used)
|
|
break;
|
|
}
|
|
result.push(used);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {!TextUtils.Text} text
|
|
* @return {!Promise}
|
|
*/
|
|
_updateTexts(uiSourceCode, text) {
|
|
const promises = [];
|
|
for (let line = 0; line < text.lineCount(); ++line) {
|
|
for (const entry of this._rawLocationsForSourceLocation(uiSourceCode, line, 0)) {
|
|
if (this._textByProvider.has(entry.contentProvider))
|
|
continue;
|
|
this._textByProvider.set(entry.contentProvider, null);
|
|
this._uiSourceCodeByContentProvider.set(entry.contentProvider, uiSourceCode);
|
|
promises.push(this._updateTextForProvider(entry.contentProvider));
|
|
}
|
|
}
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.ContentProvider} contentProvider
|
|
* @return {!Promise}
|
|
*/
|
|
async _updateTextForProvider(contentProvider) {
|
|
const content = await contentProvider.requestContent();
|
|
this._textByProvider.set(contentProvider, new TextUtils.Text(content));
|
|
}
|
|
|
|
/**
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {number} line
|
|
* @param {number} column
|
|
* @return {!Array<!Coverage.RawLocation>}
|
|
*/
|
|
_rawLocationsForSourceLocation(uiSourceCode, line, column) {
|
|
const result = [];
|
|
const contentType = uiSourceCode.contentType();
|
|
if (contentType.hasScripts()) {
|
|
let location = Bindings.debuggerWorkspaceBinding.uiLocationToRawLocation(uiSourceCode, line, column);
|
|
if (location && location.script()) {
|
|
const script = location.script();
|
|
if (script.isInlineScript() && contentType.isDocument()) {
|
|
if (comparePositions(script.lineOffset, script.columnOffset, location.lineNumber, location.columnNumber) >
|
|
0 ||
|
|
comparePositions(script.endLine, script.endColumn, location.lineNumber, location.columnNumber) <= 0) {
|
|
location = null;
|
|
} else {
|
|
location.lineNumber -= script.lineOffset;
|
|
if (!location.lineNumber)
|
|
location.columnNumber -= script.columnOffset;
|
|
}
|
|
}
|
|
if (location) {
|
|
result.push({
|
|
id: `js:${location.scriptId}`,
|
|
contentProvider: location.script(),
|
|
line: location.lineNumber,
|
|
column: location.columnNumber
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if (contentType.isStyleSheet() || contentType.isDocument()) {
|
|
const rawStyleLocations = contentType.isDocument() ?
|
|
this._documentUILocationToCSSRawLocations(uiSourceCode, line, column) :
|
|
Bindings.cssWorkspaceBinding.uiLocationToRawLocations(new Workspace.UILocation(uiSourceCode, line, column));
|
|
for (const location of rawStyleLocations) {
|
|
const header = location.header();
|
|
if (!header)
|
|
continue;
|
|
if (header.isInline && contentType.isDocument()) {
|
|
location.lineNumber -= header.startLine;
|
|
if (!location.lineNumber)
|
|
location.columnNumber -= header.startColumn;
|
|
}
|
|
result.push({
|
|
id: `css:${location.styleSheetId}`,
|
|
contentProvider: location.header(),
|
|
line: location.lineNumber,
|
|
column: location.columnNumber
|
|
});
|
|
}
|
|
}
|
|
result.sort(Coverage.CoverageDecorationManager._compareLocations);
|
|
|
|
/**
|
|
* @param {number} aLine
|
|
* @param {number} aColumn
|
|
* @param {number} bLine
|
|
* @param {number} bColumn
|
|
* @return {number}
|
|
*/
|
|
function comparePositions(aLine, aColumn, bLine, bColumn) {
|
|
return aLine - bLine || aColumn - bColumn;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* TODO(crbug.com/720162): get rid of this, use bindings.
|
|
*
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {number} line
|
|
* @param {number} column
|
|
* @return {!Array<!SDK.CSSLocation>}
|
|
*/
|
|
_documentUILocationToCSSRawLocations(uiSourceCode, line, column) {
|
|
let stylesheets = this._documentUISouceCodeToStylesheets.get(uiSourceCode);
|
|
if (!stylesheets) {
|
|
stylesheets = [];
|
|
const cssModel = this._coverageModel.target().model(SDK.CSSModel);
|
|
if (!cssModel)
|
|
return [];
|
|
for (const headerId of cssModel.styleSheetIdsForURL(uiSourceCode.url())) {
|
|
const header = cssModel.styleSheetHeaderForId(headerId);
|
|
if (header)
|
|
stylesheets.push(header);
|
|
}
|
|
stylesheets.sort(stylesheetComparator);
|
|
this._documentUISouceCodeToStylesheets.set(uiSourceCode, stylesheets);
|
|
}
|
|
const endIndex =
|
|
stylesheets.upperBound(undefined, (unused, header) => line - header.startLine || column - header.startColumn);
|
|
if (!endIndex)
|
|
return [];
|
|
const locations = [];
|
|
const last = stylesheets[endIndex - 1];
|
|
for (let index = endIndex - 1; index >= 0 && stylesheets[index].startLine === last.startLine &&
|
|
stylesheets[index].startColumn === last.startColumn;
|
|
--index)
|
|
locations.push(new SDK.CSSLocation(stylesheets[index], line, column));
|
|
|
|
return locations;
|
|
/**
|
|
* @param {!SDK.CSSStyleSheetHeader} a
|
|
* @param {!SDK.CSSStyleSheetHeader} b
|
|
* @return {number}
|
|
*/
|
|
function stylesheetComparator(a, b) {
|
|
return a.startLine - b.startLine || a.startColumn - b.startColumn || a.id.localeCompare(b.id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Coverage.RawLocation} a
|
|
* @param {!Coverage.RawLocation} b
|
|
*/
|
|
static _compareLocations(a, b) {
|
|
return a.id.localeCompare(b.id) || a.line - b.line || a.column - b.column;
|
|
}
|
|
|
|
/**
|
|
* @param {!Common.Event} event
|
|
*/
|
|
_onUISourceCodeAdded(event) {
|
|
const uiSourceCode = /** @type !Workspace.UISourceCode */ (event.data);
|
|
uiSourceCode.addLineDecoration(0, Coverage.CoverageDecorationManager._decoratorType, this);
|
|
}
|
|
};
|
|
|
|
Coverage.CoverageDecorationManager._decoratorType = 'coverage';
|
|
|
|
/**
|
|
* @implements {SourceFrame.LineDecorator}
|
|
*/
|
|
Coverage.CoverageView.LineDecorator = class {
|
|
/**
|
|
* @override
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @param {!TextEditor.CodeMirrorTextEditor} textEditor
|
|
*/
|
|
decorate(uiSourceCode, textEditor) {
|
|
const decorations = uiSourceCode.decorationsForType(Coverage.CoverageDecorationManager._decoratorType);
|
|
if (!decorations || !decorations.size) {
|
|
textEditor.uninstallGutter(Coverage.CoverageView.LineDecorator._gutterType);
|
|
return;
|
|
}
|
|
const decorationManager =
|
|
/** @type {!Coverage.CoverageDecorationManager} */ (decorations.values().next().value.data());
|
|
decorationManager.usageByLine(uiSourceCode).then(lineUsage => {
|
|
textEditor.operation(() => this._innerDecorate(textEditor, lineUsage));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {!TextEditor.CodeMirrorTextEditor} textEditor
|
|
* @param {!Array<boolean>} lineUsage
|
|
*/
|
|
_innerDecorate(textEditor, lineUsage) {
|
|
const gutterType = Coverage.CoverageView.LineDecorator._gutterType;
|
|
textEditor.uninstallGutter(gutterType);
|
|
textEditor.installGutter(gutterType, false);
|
|
for (let line = 0; line < lineUsage.length; ++line) {
|
|
// Do not decorate the line if we don't have data.
|
|
if (typeof lineUsage[line] !== 'boolean')
|
|
continue;
|
|
const className = lineUsage[line] ? 'text-editor-coverage-used-marker' : 'text-editor-coverage-unused-marker';
|
|
textEditor.setGutterDecoration(line, gutterType, createElementWithClass('div', className));
|
|
}
|
|
}
|
|
};
|
|
|
|
Coverage.CoverageView.LineDecorator._gutterType = 'CodeMirror-gutter-coverage'; |