mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-13 10:05:41 +08:00
Bug: 1006759 Change-Id: Ie1ca01eaadde94191431217f297b108dde5e8adf Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2002596 Commit-Queue: Paul Lewis <aerotwist@chromium.org> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
222 lines
6.5 KiB
JavaScript
222 lines
6.5 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 {Plugin} from './Plugin.js';
|
|
|
|
export class GutterDiffPlugin extends Plugin {
|
|
/**
|
|
* @param {!TextEditor.CodeMirrorTextEditor} textEditor
|
|
* @param {!Workspace.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();
|
|
this._workspaceDiff.subscribeToDiffChange(this._uiSourceCode, this._update, this);
|
|
this._update();
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!Workspace.UISourceCode} uiSourceCode
|
|
* @return {boolean}
|
|
*/
|
|
static accepts(uiSourceCode) {
|
|
return uiSourceCode.project().type() === 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;
|
|
}
|
|
|
|
/** @type {!Map<number, !GutterDecoration>} */
|
|
const oldDecorations = new Map();
|
|
for (let i = 0; i < this._decorations.length; ++i) {
|
|
const decoration = this._decorations[i];
|
|
const lineNumber = decoration.lineNumber();
|
|
if (lineNumber === -1) {
|
|
continue;
|
|
}
|
|
oldDecorations.set(lineNumber, decoration);
|
|
}
|
|
|
|
const diff = SourceFrame.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 = oldDecorations.diff(newDecorations, (e1, e2) => e1.type === e2.type);
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* @param {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.EditType}>} decorations
|
|
*/
|
|
_decorationsSetForTest(decorations) {
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!UI.ContextMenu} contextMenu
|
|
* @param {number} lineNumber
|
|
* @return {!Promise}
|
|
*/
|
|
async populateLineGutterContextMenu(contextMenu, lineNumber) {
|
|
GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, this._uiSourceCode);
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
* @param {!UI.ContextMenu} contextMenu
|
|
* @param {number} lineNumber
|
|
* @param {number} columnNumber
|
|
* @return {!Promise}
|
|
*/
|
|
async populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber) {
|
|
GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, this._uiSourceCode);
|
|
}
|
|
|
|
static _appendRevealDiffContextMenu(contextMenu, uiSourceCode) {
|
|
if (!WorkspaceDiff.workspaceDiff().isUISourceCodeModified(uiSourceCode)) {
|
|
return;
|
|
}
|
|
contextMenu.revealSection().appendItem(ls`Local Modifications...`, () => {
|
|
Common.Revealer.reveal(new WorkspaceDiff.DiffUILocation(uiSourceCode));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
dispose() {
|
|
for (const decoration of this._decorations) {
|
|
decoration.remove();
|
|
}
|
|
WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(this._uiSourceCode, this._update, this);
|
|
}
|
|
}
|
|
|
|
export class GutterDecoration {
|
|
/**
|
|
* @param {!TextEditor.CodeMirrorTextEditor} 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 = createElementWithClass('div', '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}
|
|
* @unrestricted
|
|
*/
|
|
export class ContextMenuProvider {
|
|
/**
|
|
* @override
|
|
* @param {!Event} event
|
|
* @param {!UI.ContextMenu} contextMenu
|
|
* @param {!Object} target
|
|
*/
|
|
appendApplicableItems(event, contextMenu, target) {
|
|
let uiSourceCode = /** @type {!Workspace.UISourceCode} */ (target);
|
|
const binding = Persistence.persistence.binding(uiSourceCode);
|
|
if (binding) {
|
|
uiSourceCode = binding.network;
|
|
}
|
|
GutterDiffPlugin._appendRevealDiffContextMenu(contextMenu, uiSourceCode);
|
|
}
|
|
}
|