mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-01 17:57:36 +08:00
For all comments that contained both the unrestricted annotation and any other potential annotation, remove only the unrestricted annotation. R=szuend@chromium.org No-Presubmit: True Bug: 1011811 Change-Id: I27dd67f657cdb1a9233d768eea81cabe2591f5f7 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2569759 Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org>
295 lines
9.1 KiB
JavaScript
295 lines
9.1 KiB
JavaScript
// Copyright 2018 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.
|
|
|
|
import * as Common from '../common/common.js';
|
|
import * as Diff from '../diff/diff.js'; // eslint-disable-line no-unused-vars
|
|
import * as Persistence from '../persistence/persistence.js';
|
|
import * as SourceFrame from '../source_frame/source_frame.js';
|
|
import * as TextEditor from '../text_editor/text_editor.js'; // eslint-disable-line no-unused-vars
|
|
import * as UI from '../ui/ui.js'; // eslint-disable-line no-unused-vars
|
|
import * as Workspace from '../workspace/workspace.js';
|
|
import * as WorkspaceDiff from '../workspace_diff/workspace_diff.js';
|
|
|
|
import {Plugin} from './Plugin.js';
|
|
|
|
export class GutterDiffPlugin extends Plugin {
|
|
/**
|
|
* @param {!SourceFrame.SourcesTextEditor.SourcesTextEditor} textEditor
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
*/
|
|
constructor(textEditor, uiSourceCode) {
|
|
super();
|
|
this._textEditor = textEditor;
|
|
this._uiSourceCode = uiSourceCode;
|
|
|
|
/** @type {!Array<!GutterDecoration>} */
|
|
this._decorations = [];
|
|
this._textEditor.installGutter(DiffGutterType, true);
|
|
this._workspaceDiff = WorkspaceDiff.WorkspaceDiff.workspaceDiff();
|
|
this._workspaceDiff.subscribeToDiffChange(this._uiSourceCode, this._update, this);
|
|
this._update();
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
* @return {boolean}
|
|
*/
|
|
static accepts(uiSourceCode) {
|
|
return uiSourceCode.project().type() === Workspace.Workspace.projectTypes.Network;
|
|
}
|
|
|
|
/**
|
|
* @param {!Array<!GutterDecoration>} removed
|
|
* @param {!Array<!GutterDecoration>} added
|
|
*/
|
|
_updateDecorations(removed, added) {
|
|
this._textEditor.operation(operation);
|
|
|
|
function operation() {
|
|
for (const decoration of removed) {
|
|
decoration.remove();
|
|
}
|
|
for (const decoration of added) {
|
|
decoration.install();
|
|
}
|
|
}
|
|
}
|
|
|
|
_update() {
|
|
if (this._uiSourceCode) {
|
|
this._workspaceDiff.requestDiff(this._uiSourceCode).then(this._innerUpdate.bind(this));
|
|
} else {
|
|
this._innerUpdate(null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {?Diff.Diff.DiffArray} lineDiff
|
|
*/
|
|
_innerUpdate(lineDiff) {
|
|
if (!lineDiff) {
|
|
this._updateDecorations(this._decorations, []);
|
|
this._decorations = [];
|
|
return;
|
|
}
|
|
|
|
const diff = SourceFrame.SourceCodeDiff.SourceCodeDiff.computeDiff(lineDiff);
|
|
|
|
/** @type {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.EditType}>} */
|
|
const newDecorations = new Map();
|
|
for (let i = 0; i < diff.length; ++i) {
|
|
const diffEntry = diff[i];
|
|
for (let lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNumber) {
|
|
newDecorations.set(lineNumber, {lineNumber: lineNumber, type: diffEntry.type});
|
|
}
|
|
}
|
|
|
|
const decorationDiff = this._calculateDecorationsDiff(newDecorations);
|
|
const addedDecorations =
|
|
decorationDiff.added.map(entry => new GutterDecoration(this._textEditor, entry.lineNumber, entry.type));
|
|
|
|
this._decorations = decorationDiff.equal.concat(addedDecorations);
|
|
this._updateDecorations(decorationDiff.removed, addedDecorations);
|
|
this._decorationsSetForTest(newDecorations);
|
|
}
|
|
|
|
/**
|
|
* @return {!Map<number, !GutterDecoration>}
|
|
*/
|
|
_decorationsByLine() {
|
|
const decorations = new Map();
|
|
for (const decoration of this._decorations) {
|
|
const lineNumber = decoration.lineNumber();
|
|
if (lineNumber !== -1) {
|
|
decorations.set(lineNumber, decoration);
|
|
}
|
|
}
|
|
return decorations;
|
|
}
|
|
|
|
/**
|
|
* @param {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.EditType}>} decorations
|
|
*/
|
|
_calculateDecorationsDiff(decorations) {
|
|
const oldDecorations = this._decorationsByLine();
|
|
const leftKeys = [...oldDecorations.keys()];
|
|
const rightKeys = [...decorations.keys()];
|
|
leftKeys.sort((a, b) => a - b);
|
|
rightKeys.sort((a, b) => a - b);
|
|
|
|
const removed = [];
|
|
const added = [];
|
|
const equal = [];
|
|
let leftIndex = 0;
|
|
let rightIndex = 0;
|
|
while (leftIndex < leftKeys.length && rightIndex < rightKeys.length) {
|
|
const leftKey = leftKeys[leftIndex];
|
|
const rightKey = rightKeys[rightIndex];
|
|
const left = oldDecorations.get(leftKey);
|
|
const right = decorations.get(rightKey);
|
|
if (!left) {
|
|
throw new Error(`No decoration with key ${leftKey}`);
|
|
}
|
|
if (!right) {
|
|
throw new Error(`No decoration with key ${rightKey}`);
|
|
}
|
|
if (leftKey === rightKey && left.type === right.type) {
|
|
equal.push(left);
|
|
++leftIndex;
|
|
++rightIndex;
|
|
} else if (leftKey <= rightKey) {
|
|
removed.push(left);
|
|
++leftIndex;
|
|
} else {
|
|
added.push(right);
|
|
++rightIndex;
|
|
}
|
|
}
|
|
while (leftIndex < leftKeys.length) {
|
|
const leftKey = leftKeys[leftIndex++];
|
|
const left = oldDecorations.get(leftKey);
|
|
if (!left) {
|
|
throw new Error(`No decoration with key ${leftKey}`);
|
|
}
|
|
removed.push(left);
|
|
}
|
|
while (rightIndex < rightKeys.length) {
|
|
const rightKey = rightKeys[rightIndex++];
|
|
const right = decorations.get(rightKey);
|
|
if (!right) {
|
|
throw new Error(`No decoration with key ${rightKey}`);
|
|
}
|
|
added.push(right);
|
|
}
|
|
return {added: added, removed: removed, equal: equal};
|
|
}
|
|
|
|
/**
|
|
* @param {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.EditType}>} decorations
|
|
*/
|
|
_decorationsSetForTest(decorations) {
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!UI.ContextMenu.ContextMenu} contextMenu
|
|
* @param {number} lineNumber
|
|
* @return {!Promise<void>}
|
|
*/
|
|
async populateLineGutterContextMenu(contextMenu, lineNumber) {
|
|
GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, this._uiSourceCode);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!UI.ContextMenu.ContextMenu} contextMenu
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {!Promise<void>}
|
|
*/
|
|
async populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber) {
|
|
GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, this._uiSourceCode);
|
|
}
|
|
|
|
/**
|
|
* @param {!UI.ContextMenu.ContextMenu} contextMenu
|
|
* @param {!Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
*/
|
|
static _appendRevealDiffContextMenu(contextMenu, uiSourceCode) {
|
|
if (!WorkspaceDiff.WorkspaceDiff.workspaceDiff().isUISourceCodeModified(uiSourceCode)) {
|
|
return;
|
|
}
|
|
contextMenu.revealSection().appendItem(ls`Local Modifications...`, () => {
|
|
Common.Revealer.reveal(new WorkspaceDiff.WorkspaceDiff.DiffUILocation(uiSourceCode));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
dispose() {
|
|
for (const decoration of this._decorations) {
|
|
decoration.remove();
|
|
}
|
|
WorkspaceDiff.WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(this._uiSourceCode, this._update, this);
|
|
}
|
|
}
|
|
|
|
export class GutterDecoration {
|
|
/**
|
|
* @param {!SourceFrame.SourcesTextEditor.SourcesTextEditor} textEditor
|
|
* @param {number} lineNumber
|
|
* @param {!SourceFrame.SourceCodeDiff.EditType} type
|
|
*/
|
|
constructor(textEditor, lineNumber, type) {
|
|
this._textEditor = textEditor;
|
|
this._position = this._textEditor.textEditorPositionHandle(lineNumber, 0);
|
|
this._className = '';
|
|
if (type === SourceFrame.SourceCodeDiff.EditType.Insert) {
|
|
this._className = 'diff-entry-insert';
|
|
} else if (type === SourceFrame.SourceCodeDiff.EditType.Delete) {
|
|
this._className = 'diff-entry-delete';
|
|
} else if (type === SourceFrame.SourceCodeDiff.EditType.Modify) {
|
|
this._className = 'diff-entry-modify';
|
|
}
|
|
this.type = type;
|
|
}
|
|
|
|
/**
|
|
* @return {number}
|
|
*/
|
|
lineNumber() {
|
|
const location = this._position.resolve();
|
|
if (!location) {
|
|
return -1;
|
|
}
|
|
return location.lineNumber;
|
|
}
|
|
|
|
install() {
|
|
const location = this._position.resolve();
|
|
if (!location) {
|
|
return;
|
|
}
|
|
const element = document.createElement('div');
|
|
element.classList.add('diff-marker');
|
|
element.textContent = '\xA0';
|
|
this._textEditor.setGutterDecoration(location.lineNumber, DiffGutterType, element);
|
|
this._textEditor.toggleLineClass(location.lineNumber, this._className, true);
|
|
}
|
|
|
|
remove() {
|
|
const location = this._position.resolve();
|
|
if (!location) {
|
|
return;
|
|
}
|
|
this._textEditor.setGutterDecoration(location.lineNumber, DiffGutterType, null);
|
|
this._textEditor.toggleLineClass(location.lineNumber, this._className, false);
|
|
}
|
|
}
|
|
|
|
/** @type {string} */
|
|
export const DiffGutterType = 'CodeMirror-gutter-diff';
|
|
|
|
/**
|
|
* @implements {UI.ContextMenu.Provider}
|
|
*/
|
|
export class ContextMenuProvider {
|
|
/**
|
|
* @override
|
|
* @param {!Event} event
|
|
* @param {!UI.ContextMenu.ContextMenu} contextMenu
|
|
* @param {!Object} target
|
|
*/
|
|
appendApplicableItems(event, contextMenu, target) {
|
|
let uiSourceCode = /** @type {!Workspace.UISourceCode.UISourceCode} */ (target);
|
|
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(uiSourceCode);
|
|
if (binding) {
|
|
uiSourceCode = binding.network;
|
|
}
|
|
GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, uiSourceCode);
|
|
}
|
|
}
|