mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-09 05:08:28 +08:00
There are severals places in the DevTools frontend UI where we need
to update existing strings to ensure that they are localizable.
1. Updated strings that are conditionaly plural into their singular and
plural forms.
2. Updated strings that use concatenation to instead use variable
substitution.
3. Fixed a couple other minor issues.
Regarding the use of variable substitution instead of concatenation.
Consider:
Common.UIString(palette.colors[i] +
'. Long-click to show alternate shades.');
Here, the location of the color isn't captured, instead the actual hex
value is. What we really need is a string with a marker for the
variable substitution.
Instead, we should do the following:
Common.UIString('%s. Long-click to show alternate shades.',
palette.colors[i]);
Or,
ls`${palette.colors[i]}. Long-click to show alternate shades.`;
Change-Id: Ica7253f7c69e9a2478452d0a944147680b4ca8d1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1504254
Commit-Queue: Lorne Mitchell <lomitch@microsoft.com>
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Reviewed-by: Joel Einbinder <einbinder@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#638193}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 5e6c21ed70371dcdb04b9f86552ef8537c5e683f
352 lines
12 KiB
JavaScript
352 lines
12 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.
|
|
|
|
Changes.ChangesView = class extends UI.VBox {
|
|
constructor() {
|
|
super(true);
|
|
this.registerRequiredCSS('changes/changesView.css');
|
|
const splitWidget = new UI.SplitWidget(true /* vertical */, false /* sidebar on left */);
|
|
const mainWidget = new UI.Widget();
|
|
splitWidget.setMainWidget(mainWidget);
|
|
splitWidget.show(this.contentElement);
|
|
|
|
this._emptyWidget = new UI.EmptyWidget('');
|
|
this._emptyWidget.show(mainWidget.element);
|
|
|
|
this._workspaceDiff = WorkspaceDiff.workspaceDiff();
|
|
this._changesSidebar = new Changes.ChangesSidebar(this._workspaceDiff);
|
|
this._changesSidebar.addEventListener(
|
|
Changes.ChangesSidebar.Events.SelectedUISourceCodeChanged, this._selectedUISourceCodeChanged, this);
|
|
splitWidget.setSidebarWidget(this._changesSidebar);
|
|
|
|
/** @type {?Workspace.UISourceCode} */
|
|
this._selectedUISourceCode = null;
|
|
|
|
/** @type {!Array<!Changes.ChangesView.Row>} */
|
|
this._diffRows = [];
|
|
|
|
this._maxLineDigits = 1;
|
|
|
|
this._editor = new TextEditor.CodeMirrorTextEditor({
|
|
lineNumbers: true,
|
|
lineWrapping: false,
|
|
maxHighlightLength: Infinity // This is to avoid CodeMirror bailing out of highlighting big diffs.
|
|
});
|
|
this._editor.setReadOnly(true);
|
|
this._editor.show(mainWidget.element.createChild('div', 'editor-container'));
|
|
this._editor.hideWidget();
|
|
|
|
this._editor.element.addEventListener('click', this._click.bind(this), false);
|
|
|
|
this._toolbar = new UI.Toolbar('changes-toolbar', mainWidget.element);
|
|
const revertButton = new UI.ToolbarButton(Common.UIString('Revert all changes'), 'largeicon-undo');
|
|
revertButton.addEventListener(UI.ToolbarButton.Events.Click, this._revert.bind(this));
|
|
this._toolbar.appendToolbarItem(revertButton);
|
|
this._diffStats = new UI.ToolbarText('');
|
|
this._toolbar.appendToolbarItem(this._diffStats);
|
|
this._toolbar.setEnabled(false);
|
|
|
|
this._hideDiff(ls`No changes`);
|
|
this._selectedUISourceCodeChanged();
|
|
}
|
|
|
|
_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())
|
|
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
|
|
*/
|
|
_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(ls`Binary data`);
|
|
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(ls`No changes`);
|
|
return;
|
|
}
|
|
|
|
let insertions = 0;
|
|
let deletions = 0;
|
|
let currentLineNumber = 0;
|
|
let baselineLineNumber = 0;
|
|
const paddingLines = 3;
|
|
const originalLines = [];
|
|
const currentLines = [];
|
|
|
|
for (let i = 0; i < diff.length; ++i) {
|
|
const token = diff[i];
|
|
switch (token[0]) {
|
|
case Diff.Diff.Operation.Equal:
|
|
this._diffRows.pushAll(createEqualRows(token[1], i === 0, i === diff.length - 1));
|
|
originalLines.pushAll(token[1]);
|
|
currentLines.pushAll(token[1]);
|
|
break;
|
|
case Diff.Diff.Operation.Insert:
|
|
for (const line of token[1])
|
|
this._diffRows.push(createRow(line, Changes.ChangesView.RowType.Addition));
|
|
insertions += token[1].length;
|
|
currentLines.pushAll(token[1]);
|
|
break;
|
|
case Diff.Diff.Operation.Delete:
|
|
deletions += token[1].length;
|
|
originalLines.pushAll(token[1]);
|
|
if (diff[i + 1] && diff[i + 1][0] === Diff.Diff.Operation.Insert) {
|
|
i++;
|
|
this._diffRows.pushAll(createModifyRows(token[1].join('\n'), diff[i][1].join('\n')));
|
|
insertions += diff[i][1].length;
|
|
currentLines.pushAll(diff[i][1]);
|
|
} else {
|
|
for (const line of token[1])
|
|
this._diffRows.push(createRow(line, Changes.ChangesView.RowType.Deletion));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
this._maxLineDigits = Math.ceil(Math.log10(Math.max(currentLineNumber, baselineLineNumber)));
|
|
|
|
let insertionText = '';
|
|
if (insertions === 1)
|
|
insertionText = ls`${insertions} insertion (+),`;
|
|
else
|
|
insertionText = ls`${insertions} insertions (+),`;
|
|
|
|
let deletionText = '';
|
|
if (deletions === 1)
|
|
deletionText = ls`${deletions} deletion (-)`;
|
|
else
|
|
deletionText = ls`${deletions} 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} */ (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));
|
|
});
|
|
|
|
/**
|
|
* @param {!Array<string>} lines
|
|
* @param {boolean} atStart
|
|
* @param {boolean} atEnd
|
|
* @return {!Array<!Changes.ChangesView.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], Changes.ChangesView.RowType.Equal));
|
|
if (lines.length > paddingLines * 2 + 1 && !atEnd) {
|
|
equalRows.push(createRow(
|
|
Common.UIString('( \u2026 Skipping %d matching lines \u2026 )', lines.length - paddingLines * 2),
|
|
Changes.ChangesView.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], Changes.ChangesView.RowType.Equal));
|
|
}
|
|
return equalRows;
|
|
}
|
|
|
|
/**
|
|
* @param {string} before
|
|
* @param {string} after
|
|
* @return {!Array<!Changes.ChangesView.Row>}}
|
|
*/
|
|
function createModifyRows(before, after) {
|
|
const internalDiff = Diff.Diff.charDiff(before, after, true /* cleanup diff */);
|
|
const deletionRows = [createRow('', Changes.ChangesView.RowType.Deletion)];
|
|
const insertionRows = [createRow('', Changes.ChangesView.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('', Changes.ChangesView.RowType.Deletion));
|
|
if (i > 0 && type !== Diff.Diff.Operation.Delete)
|
|
insertionRows.push(createRow('', Changes.ChangesView.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 {!Changes.ChangesView.RowType} type
|
|
* @return {!Changes.ChangesView.Row}
|
|
*/
|
|
function createRow(text, type) {
|
|
if (type === Changes.ChangesView.RowType.Addition)
|
|
currentLineNumber++;
|
|
if (type === Changes.ChangesView.RowType.Deletion)
|
|
baselineLineNumber++;
|
|
if (type === Changes.ChangesView.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 === Changes.ChangesView.RowType.Deletion;
|
|
let showCurrentNumber = row.type === Changes.ChangesView.RowType.Addition;
|
|
if (row.type === Changes.ChangesView.RowType.Equal) {
|
|
showBaseNumber = true;
|
|
showCurrentNumber = true;
|
|
}
|
|
const base = showBaseNumber ? numberToStringWithSpacesPadding(row.baselineLineNumber, this._maxLineDigits) :
|
|
spacesPadding(this._maxLineDigits);
|
|
const current = showCurrentNumber ? numberToStringWithSpacesPadding(row.currentLineNumber, this._maxLineDigits) :
|
|
spacesPadding(this._maxLineDigits);
|
|
return base + spacesPadding(1) + current;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @typedef {!{
|
|
* baselineLineNumber: number,
|
|
* currentLineNumber: number,
|
|
* tokens: !Array<!{text: string, className: string}>,
|
|
* type: !Changes.ChangesView.RowType
|
|
* }}
|
|
*/
|
|
Changes.ChangesView.Row;
|
|
|
|
/** @enum {string} */
|
|
Changes.ChangesView.RowType = {
|
|
Deletion: 'deletion',
|
|
Addition: 'addition',
|
|
Equal: 'equal',
|
|
Spacer: 'spacer'
|
|
};
|
|
|
|
/**
|
|
* @implements {Common.Revealer}
|
|
*/
|
|
Changes.ChangesView.DiffUILocationRevealer = class {
|
|
/**
|
|
* @override
|
|
* @param {!Object} diffUILocation
|
|
* @param {boolean=} omitFocus
|
|
* @return {!Promise}
|
|
*/
|
|
async reveal(diffUILocation, omitFocus) {
|
|
if (!(diffUILocation instanceof WorkspaceDiff.DiffUILocation))
|
|
throw new Error('Internal error: not a diff ui location');
|
|
/** @type {!Changes.ChangesView} */
|
|
const changesView = self.runtime.sharedInstance(Changes.ChangesView);
|
|
await UI.viewManager.showView('changes.changes');
|
|
changesView._changesSidebar.selectUISourceCode(diffUILocation.uiSourceCode, omitFocus);
|
|
}
|
|
};
|