mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-09-30 17:27:22 +08:00
This reverts commit9e5ab71e50. Reason for revert: The Cl was reverted due to devtools pak file limitation. That issue has been fixed in https://chromium-review.googlesource.com/c/chromium/src/+/2558341 Original change's description: > Revert "[Loc] Migrate changes tool to localization V2" > > This reverts commitabcb8f7aa8. > > Reason for revert: Consistently causing autoroll issues when rolling devtools-frontend into Chromium - see https://chromium-review.googlesource.com/c/chromium/src/+/2554521 for an example. > > To recreate this, do a debug build of Chromium with this patch applied to DevTools. It's causing an issue with duplicate keys in the pak files. No DevTools Frontend builds do a debug build of Chromium with the DevTools frontend patch applied so our CQ will be green despite causing an issue. > > You can run this locally in Chromium to reproduce via: > > autoninja -C out/Default chrome:packed_resources_extra > > Which will show the error. > > Original change's description: > > [Loc] Migrate changes tool to localization V2 > > > > Migrate changes tool > > Tested with en-XL: https://imgur.com/a/hFDBjQV > > > > > > Bug: 1136655 > > Change-Id: Ic434c3085b9e2f2cf44583cca7ee1ce8f1d412f2 > > Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2548688 > > Commit-Queue: Christy Chen <chrche@microsoft.com> > > Reviewed-by: Simon Zünd <szuend@chromium.org> > > TBR=szuend@chromium.org,chrche@microsoft.com > > Change-Id: I94b27434562f6a6bff3be381ab8a5802bd8b03f9 > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: 1136655 > Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2557044 > Commit-Queue: Jack Franklin <jacktfranklin@chromium.org> > Reviewed-by: Jack Franklin <jacktfranklin@chromium.org> TBR=szuend@chromium.org,chrche@microsoft.com,jacktfranklin@chromium.org # Not skipping CQ checks because original CL landed > 1 day ago. Bug: 1136655 Change-Id: I35166a6f4f38462073b933c01c169b3c8d14311b Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2567344 Reviewed-by: Simon Zünd <szuend@chromium.org> Reviewed-by: Christy Chen <chrche@microsoft.com> Commit-Queue: Christy Chen <chrche@microsoft.com>
460 lines
14 KiB
JavaScript
460 lines
14 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.
|
|
|
|
import * as Common from '../common/common.js';
|
|
import * as Diff from '../diff/diff.js';
|
|
import * as i18n from '../i18n/i18n.js';
|
|
import * as UI from '../ui/ui.js';
|
|
import * as Workspace from '../workspace/workspace.js'; // eslint-disable-line no-unused-vars
|
|
import * as WorkspaceDiff from '../workspace_diff/workspace_diff.js';
|
|
|
|
import {ChangesSidebar, Events} from './ChangesSidebar.js';
|
|
import {ChangesTextEditor} from './ChangesTextEditor.js';
|
|
|
|
export const UIStrings = {
|
|
/**
|
|
*@description Screen-reader accessible name for the code editor in the Changes tool showing the user's changes.
|
|
*/
|
|
changesDiffViewer: 'Changes diff viewer',
|
|
/**
|
|
*@description Screen reader/tooltip label for a button in the Changes tool that reverts all changes to the currently open file.
|
|
*/
|
|
revertAllChangesToCurrentFile: 'Revert all changes to current file',
|
|
/**
|
|
*@description Text in Changes View of the Changes tab
|
|
*/
|
|
noChanges: 'No changes',
|
|
/**
|
|
*@description Text in Changes View of the Changes tab
|
|
*/
|
|
binaryData: 'Binary data',
|
|
/**
|
|
*@description Insertion text in Changes View of the Changes tab
|
|
*@example {1} PH1
|
|
*/
|
|
sInsertion: '{PH1} insertion (+),',
|
|
/**
|
|
*@description Insertion text in Changes View of the Changes tab
|
|
*@example {2} PH1
|
|
*/
|
|
sInsertions: '{PH1} insertions (+),',
|
|
/**
|
|
*@description Deletion text in Changes View of the Changes tab
|
|
*@example {1} PH1
|
|
*/
|
|
sDeletion: '{PH1} deletion (-)',
|
|
/**
|
|
*@description Deletion text in Changes View of the Changes tab
|
|
*@example {2} PH1
|
|
*/
|
|
sDeletions: '{PH1} deletions (-)',
|
|
/**
|
|
*@description Text in Changes View of the Changes tab
|
|
*@example {2} PH1
|
|
*/
|
|
SkippingDMatchingLines: '( … Skipping {PH1} matching lines … )',
|
|
};
|
|
const str_ = i18n.i18n.registerUIStrings('changes/ChangesView.js', UIStrings);
|
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
|
|
/** @type {!ChangesView} */
|
|
let changesViewInstance;
|
|
|
|
export class ChangesView extends UI.Widget.VBox {
|
|
/**
|
|
* @private
|
|
*/
|
|
constructor() {
|
|
super(true);
|
|
this.registerRequiredCSS('changes/changesView.css', {enableLegacyPatching: true});
|
|
const splitWidget = new UI.SplitWidget.SplitWidget(true /* vertical */, false /* sidebar on left */);
|
|
const mainWidget = new UI.Widget.Widget();
|
|
splitWidget.setMainWidget(mainWidget);
|
|
splitWidget.show(this.contentElement);
|
|
|
|
this._emptyWidget = new UI.EmptyWidget.EmptyWidget('');
|
|
this._emptyWidget.show(mainWidget.element);
|
|
|
|
this._workspaceDiff = WorkspaceDiff.WorkspaceDiff.workspaceDiff();
|
|
this._changesSidebar = new ChangesSidebar(this._workspaceDiff);
|
|
this._changesSidebar.addEventListener(Events.SelectedUISourceCodeChanged, this._selectedUISourceCodeChanged, this);
|
|
splitWidget.setSidebarWidget(this._changesSidebar);
|
|
|
|
/** @type {?Workspace.UISourceCode.UISourceCode} */
|
|
this._selectedUISourceCode = null;
|
|
|
|
/** @type {!Array<!Row>} */
|
|
this._diffRows = [];
|
|
|
|
this._maxLineDigits = 1;
|
|
|
|
this._editor = new ChangesTextEditor({
|
|
bracketMatchingSetting: undefined,
|
|
devtoolsAccessibleName: i18nString(UIStrings.changesDiffViewer),
|
|
lineNumbers: true,
|
|
lineWrapping: false,
|
|
mimeType: undefined,
|
|
autoHeight: undefined,
|
|
padBottom: undefined,
|
|
maxHighlightLength: Infinity, // Avoid CodeMirror bailing out of highlighting big diffs.
|
|
placeholder: undefined,
|
|
lineWiseCopyCut: undefined,
|
|
inputStyle: undefined,
|
|
});
|
|
this._editor.setReadOnly(true);
|
|
const editorContainer = mainWidget.element.createChild('div', 'editor-container');
|
|
UI.ARIAUtils.markAsTabpanel(editorContainer);
|
|
this._editor.show(editorContainer);
|
|
this._editor.hideWidget();
|
|
|
|
self.onInvokeElement(this._editor.element, this._click.bind(this));
|
|
|
|
this._toolbar = new UI.Toolbar.Toolbar('changes-toolbar', mainWidget.element);
|
|
const revertButton =
|
|
new UI.Toolbar.ToolbarButton(i18nString(UIStrings.revertAllChangesToCurrentFile), 'largeicon-undo');
|
|
revertButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._revert.bind(this));
|
|
this._toolbar.appendToolbarItem(revertButton);
|
|
this._diffStats = new UI.Toolbar.ToolbarText('');
|
|
this._toolbar.appendToolbarItem(this._diffStats);
|
|
this._toolbar.setEnabled(false);
|
|
|
|
this._hideDiff(i18nString(UIStrings.noChanges));
|
|
this._selectedUISourceCodeChanged();
|
|
}
|
|
|
|
/**
|
|
* @param {{forceNew: ?boolean}} opts
|
|
*/
|
|
static instance(opts = {forceNew: null}) {
|
|
const {forceNew} = opts;
|
|
if (!changesViewInstance || forceNew) {
|
|
changesViewInstance = new ChangesView();
|
|
}
|
|
|
|
return changesViewInstance;
|
|
}
|
|
|
|
_selectedUISourceCodeChanged() {
|
|
this._revealUISourceCode(this._changesSidebar.selectedUISourceCode());
|
|
}
|
|
|
|
_revert() {
|
|
const uiSourceCode = this._selectedUISourceCode;
|
|
if (!uiSourceCode) {
|
|
return;
|
|
}
|
|
this._workspaceDiff.revertToOriginal(uiSourceCode);
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_click(event) {
|
|
const selection = this._editor.selection();
|
|
if (!selection.isEmpty() || !this._selectedUISourceCode) {
|
|
return;
|
|
}
|
|
const row = this._diffRows[selection.startLine];
|
|
Common.Revealer.reveal(
|
|
this._selectedUISourceCode.uiLocation(row.currentLineNumber - 1, selection.startColumn), false);
|
|
event.consume(true);
|
|
}
|
|
|
|
/**
|
|
* @param {?Workspace.UISourceCode.UISourceCode} uiSourceCode
|
|
*/
|
|
_revealUISourceCode(uiSourceCode) {
|
|
if (this._selectedUISourceCode === uiSourceCode) {
|
|
return;
|
|
}
|
|
|
|
if (this._selectedUISourceCode) {
|
|
this._workspaceDiff.unsubscribeFromDiffChange(this._selectedUISourceCode, this._refreshDiff, this);
|
|
}
|
|
if (uiSourceCode && this.isShowing()) {
|
|
this._workspaceDiff.subscribeToDiffChange(uiSourceCode, this._refreshDiff, this);
|
|
}
|
|
|
|
this._selectedUISourceCode = uiSourceCode;
|
|
this._refreshDiff();
|
|
}
|
|
|
|
/**
|
|
* @override
|
|
*/
|
|
wasShown() {
|
|
this._refreshDiff();
|
|
}
|
|
|
|
_refreshDiff() {
|
|
if (!this.isShowing()) {
|
|
return;
|
|
}
|
|
|
|
if (!this._selectedUISourceCode) {
|
|
this._renderDiffRows(null);
|
|
return;
|
|
}
|
|
const uiSourceCode = this._selectedUISourceCode;
|
|
if (!uiSourceCode.contentType().isTextType()) {
|
|
this._hideDiff(i18nString(UIStrings.binaryData));
|
|
return;
|
|
}
|
|
this._workspaceDiff.requestDiff(uiSourceCode).then(diff => {
|
|
if (this._selectedUISourceCode !== uiSourceCode) {
|
|
return;
|
|
}
|
|
this._renderDiffRows(diff);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {string} message
|
|
*/
|
|
_hideDiff(message) {
|
|
this._diffStats.setText('');
|
|
this._toolbar.setEnabled(false);
|
|
this._editor.hideWidget();
|
|
this._emptyWidget.text = message;
|
|
this._emptyWidget.showWidget();
|
|
}
|
|
|
|
/**
|
|
* @param {?Diff.Diff.DiffArray} diff
|
|
*/
|
|
_renderDiffRows(diff) {
|
|
this._diffRows = [];
|
|
|
|
if (!diff || (diff.length === 1 && diff[0][0] === Diff.Diff.Operation.Equal)) {
|
|
this._hideDiff(i18nString(UIStrings.noChanges));
|
|
return;
|
|
}
|
|
|
|
let insertions = 0;
|
|
let deletions = 0;
|
|
let currentLineNumber = 0;
|
|
let baselineLineNumber = 0;
|
|
const paddingLines = 3;
|
|
|
|
/** @type {!Array<string>} */
|
|
const originalLines = [];
|
|
/** @type {!Array<string>} */
|
|
const currentLines = [];
|
|
|
|
for (let i = 0; i < diff.length; ++i) {
|
|
const token = diff[i];
|
|
switch (token[0]) {
|
|
case Diff.Diff.Operation.Equal:
|
|
this._diffRows.push(...createEqualRows(token[1], i === 0, i === diff.length - 1));
|
|
originalLines.push(...token[1]);
|
|
currentLines.push(...token[1]);
|
|
break;
|
|
case Diff.Diff.Operation.Insert:
|
|
for (const line of token[1]) {
|
|
this._diffRows.push(createRow(line, RowType.Addition));
|
|
}
|
|
insertions += token[1].length;
|
|
currentLines.push(...token[1]);
|
|
break;
|
|
case Diff.Diff.Operation.Delete:
|
|
deletions += token[1].length;
|
|
originalLines.push(...token[1]);
|
|
if (diff[i + 1] && diff[i + 1][0] === Diff.Diff.Operation.Insert) {
|
|
i++;
|
|
this._diffRows.push(...createModifyRows(token[1].join('\n'), diff[i][1].join('\n')));
|
|
insertions += diff[i][1].length;
|
|
currentLines.push(...diff[i][1]);
|
|
} else {
|
|
for (const line of token[1]) {
|
|
this._diffRows.push(createRow(line, RowType.Deletion));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
this._maxLineDigits = Math.ceil(Math.log10(Math.max(currentLineNumber, baselineLineNumber)));
|
|
|
|
let insertionText = '';
|
|
if (insertions === 1) {
|
|
insertionText = i18nString(UIStrings.sInsertion, {PH1: insertions});
|
|
} else {
|
|
insertionText = i18nString(UIStrings.sInsertions, {PH1: insertions});
|
|
}
|
|
|
|
let deletionText = '';
|
|
if (deletions === 1) {
|
|
deletionText = i18nString(UIStrings.sDeletion, {PH1: deletions});
|
|
} else {
|
|
deletionText = i18nString(UIStrings.sDeletions, {PH1: deletions});
|
|
}
|
|
|
|
this._diffStats.setText(`${insertionText} ${deletionText}`);
|
|
this._toolbar.setEnabled(true);
|
|
this._emptyWidget.hideWidget();
|
|
|
|
this._editor.operation(() => {
|
|
this._editor.showWidget();
|
|
this._editor.setHighlightMode({
|
|
name: 'devtools-diff',
|
|
diffRows: this._diffRows,
|
|
mimeType: /** @type {!Workspace.UISourceCode.UISourceCode} */ (this._selectedUISourceCode).mimeType(),
|
|
baselineLines: originalLines,
|
|
currentLines: currentLines
|
|
});
|
|
this._editor.setText(this._diffRows.map(row => row.tokens.map(t => t.text).join('')).join('\n'));
|
|
this._editor.setLineNumberFormatter(this._lineFormatter.bind(this));
|
|
this._editor.updateDiffGutter(this._diffRows);
|
|
});
|
|
|
|
/**
|
|
* @param {!Array<string>} lines
|
|
* @param {boolean} atStart
|
|
* @param {boolean} atEnd
|
|
* @return {!Array<!Row>}}
|
|
*/
|
|
function createEqualRows(lines, atStart, atEnd) {
|
|
const equalRows = [];
|
|
if (!atStart) {
|
|
for (let i = 0; i < paddingLines && i < lines.length; i++) {
|
|
equalRows.push(createRow(lines[i], RowType.Equal));
|
|
}
|
|
if (lines.length > paddingLines * 2 + 1 && !atEnd) {
|
|
equalRows.push(createRow(
|
|
i18nString(UIStrings.SkippingDMatchingLines, {PH1: (lines.length - paddingLines * 2)}), RowType.Spacer));
|
|
}
|
|
}
|
|
if (!atEnd) {
|
|
const start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : paddingLines);
|
|
let skip = lines.length - paddingLines - 1;
|
|
if (!atStart) {
|
|
skip -= paddingLines;
|
|
}
|
|
if (skip > 0) {
|
|
baselineLineNumber += skip;
|
|
currentLineNumber += skip;
|
|
}
|
|
|
|
for (let i = start; i < lines.length; i++) {
|
|
equalRows.push(createRow(lines[i], RowType.Equal));
|
|
}
|
|
}
|
|
return equalRows;
|
|
}
|
|
|
|
/**
|
|
* @param {string} before
|
|
* @param {string} after
|
|
* @return {!Array<!Row>}}
|
|
*/
|
|
function createModifyRows(before, after) {
|
|
const internalDiff = Diff.Diff.DiffWrapper.charDiff(before, after, true /* cleanup diff */);
|
|
const deletionRows = [createRow('', RowType.Deletion)];
|
|
const insertionRows = [createRow('', RowType.Addition)];
|
|
|
|
for (const token of internalDiff) {
|
|
const text = token[1];
|
|
const type = token[0];
|
|
const className = type === Diff.Diff.Operation.Equal ? '' : 'inner-diff';
|
|
const lines = text.split('\n');
|
|
for (let i = 0; i < lines.length; i++) {
|
|
if (i > 0 && type !== Diff.Diff.Operation.Insert) {
|
|
deletionRows.push(createRow('', RowType.Deletion));
|
|
}
|
|
if (i > 0 && type !== Diff.Diff.Operation.Delete) {
|
|
insertionRows.push(createRow('', RowType.Addition));
|
|
}
|
|
if (!lines[i]) {
|
|
continue;
|
|
}
|
|
if (type !== Diff.Diff.Operation.Insert) {
|
|
deletionRows[deletionRows.length - 1].tokens.push({text: lines[i], className});
|
|
}
|
|
if (type !== Diff.Diff.Operation.Delete) {
|
|
insertionRows[insertionRows.length - 1].tokens.push({text: lines[i], className});
|
|
}
|
|
}
|
|
}
|
|
return deletionRows.concat(insertionRows);
|
|
}
|
|
|
|
/**
|
|
* @param {string} text
|
|
* @param {!RowType} type
|
|
* @return {!Row}
|
|
*/
|
|
function createRow(text, type) {
|
|
if (type === RowType.Addition) {
|
|
currentLineNumber++;
|
|
}
|
|
if (type === RowType.Deletion) {
|
|
baselineLineNumber++;
|
|
}
|
|
if (type === RowType.Equal) {
|
|
baselineLineNumber++;
|
|
currentLineNumber++;
|
|
}
|
|
|
|
return {baselineLineNumber, currentLineNumber, tokens: text ? [{text, className: 'inner-diff'}] : [], type};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} lineNumber
|
|
* @return {string}
|
|
*/
|
|
_lineFormatter(lineNumber) {
|
|
const row = this._diffRows[lineNumber - 1];
|
|
let showBaseNumber = row.type === RowType.Deletion;
|
|
let showCurrentNumber = row.type === RowType.Addition;
|
|
if (row.type === RowType.Equal) {
|
|
showBaseNumber = true;
|
|
showCurrentNumber = true;
|
|
}
|
|
const baseText = showBaseNumber ? String(row.baselineLineNumber) : '';
|
|
const base = baseText.padStart(this._maxLineDigits, '\xA0');
|
|
const currentText = showCurrentNumber ? String(row.currentLineNumber) : '';
|
|
const current = currentText.padStart(this._maxLineDigits, '\xA0');
|
|
return base + '\xA0' + current;
|
|
}
|
|
}
|
|
|
|
/** @enum {string} */
|
|
export const RowType = {
|
|
Deletion: 'deletion',
|
|
Addition: 'addition',
|
|
Equal: 'equal',
|
|
Spacer: 'spacer'
|
|
};
|
|
|
|
/**
|
|
* @implements {Common.Revealer.Revealer}
|
|
*/
|
|
export class DiffUILocationRevealer {
|
|
/**
|
|
* @override
|
|
* @param {!Object} diffUILocation
|
|
* @param {boolean=} omitFocus
|
|
* @return {!Promise<void>}
|
|
*/
|
|
async reveal(diffUILocation, omitFocus) {
|
|
if (!(diffUILocation instanceof WorkspaceDiff.WorkspaceDiff.DiffUILocation)) {
|
|
throw new Error('Internal error: not a diff ui location');
|
|
}
|
|
await UI.ViewManager.ViewManager.instance().showView('changes.changes');
|
|
ChangesView.instance()._changesSidebar.selectUISourceCode(diffUILocation.uiSourceCode, omitFocus);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @typedef {!{
|
|
* baselineLineNumber: number,
|
|
* currentLineNumber: number,
|
|
* tokens: !Array<!{text: string, className: string}>,
|
|
* type: !RowType
|
|
* }}
|
|
*/
|
|
// @ts-ignore typedef
|
|
export let Row;
|