mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-01 01:38:07 +08:00
R=szuend@chromium.org Bug: 1011811 Change-Id: I891dfb295e13676dd97d4a9fec97ce08a22ae279 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2550064 Reviewed-by: Simon Zünd <szuend@chromium.org> Commit-Queue: Yang Guo <yangguo@chromium.org>
153 lines
4.4 KiB
JavaScript
153 lines
4.4 KiB
JavaScript
// Copyright 2016 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 Diff from '../diff/diff.js';
|
|
import * as TextEditor from '../text_editor/text_editor.js'; // eslint-disable-line no-unused-vars
|
|
import {SourcesTextEditor} from './SourcesTextEditor.js'; // eslint-disable-line no-unused-vars
|
|
|
|
export class SourceCodeDiff {
|
|
/**
|
|
* @param {!SourcesTextEditor} textEditor
|
|
*/
|
|
constructor(textEditor) {
|
|
this._textEditor = textEditor;
|
|
/** @type {!Array<!TextEditor.CodeMirrorTextEditor.TextEditorPositionHandle>}*/
|
|
this._animatedLines = [];
|
|
/** @type {?number} */
|
|
this._animationTimeout = null;
|
|
}
|
|
|
|
/**
|
|
* @param {?string} oldContent
|
|
* @param {?string} newContent
|
|
*/
|
|
highlightModifiedLines(oldContent, newContent) {
|
|
if (typeof oldContent !== 'string' || typeof newContent !== 'string') {
|
|
return;
|
|
}
|
|
|
|
const diff =
|
|
SourceCodeDiff.computeDiff(Diff.Diff.DiffWrapper.lineDiff(oldContent.split('\n'), newContent.split('\n')));
|
|
const changedLines = [];
|
|
for (let i = 0; i < diff.length; ++i) {
|
|
const diffEntry = diff[i];
|
|
if (diffEntry.type === EditType.Delete) {
|
|
continue;
|
|
}
|
|
for (let lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNumber) {
|
|
const position = this._textEditor.textEditorPositionHandle(lineNumber, 0);
|
|
if (position) {
|
|
changedLines.push(position);
|
|
}
|
|
}
|
|
}
|
|
this._updateHighlightedLines(changedLines);
|
|
this._animationTimeout = window.setTimeout(
|
|
this._updateHighlightedLines.bind(this, []), 400); // // Keep this timeout in sync with sourcesView.css.
|
|
}
|
|
|
|
/**
|
|
* @param {!Array<!TextEditor.CodeMirrorTextEditor.TextEditorPositionHandle>} newLines
|
|
*/
|
|
_updateHighlightedLines(newLines) {
|
|
if (this._animationTimeout) {
|
|
clearTimeout(this._animationTimeout);
|
|
}
|
|
this._animationTimeout = null;
|
|
this._textEditor.operation(operation.bind(this));
|
|
|
|
/**
|
|
* @this {SourceCodeDiff}
|
|
*/
|
|
function operation() {
|
|
toggleLines.call(this, false);
|
|
this._animatedLines = newLines;
|
|
toggleLines.call(this, true);
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} value
|
|
* @this {SourceCodeDiff}
|
|
*/
|
|
function toggleLines(value) {
|
|
for (let i = 0; i < this._animatedLines.length; ++i) {
|
|
const location = this._animatedLines[i].resolve();
|
|
if (location) {
|
|
this._textEditor.toggleLineClass(location.lineNumber, 'highlight-line-modification', value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Diff.Diff.DiffArray} diff
|
|
* @return {!Array<!{type: !EditType, from: number, to: number}>}
|
|
*/
|
|
static computeDiff(diff) {
|
|
/** @type {!Array<!{type: !EditType, from: number, to: number}>} */
|
|
const result = [];
|
|
let hasAdded = false;
|
|
let hasRemoved = false;
|
|
let blockStartLineNumber = 0;
|
|
let currentLineNumber = 0;
|
|
let isInsideBlock = false;
|
|
for (let i = 0; i < diff.length; ++i) {
|
|
const token = diff[i];
|
|
if (token[0] === Diff.Diff.Operation.Equal) {
|
|
if (isInsideBlock) {
|
|
flush();
|
|
}
|
|
currentLineNumber += token[1].length;
|
|
continue;
|
|
}
|
|
|
|
if (!isInsideBlock) {
|
|
isInsideBlock = true;
|
|
blockStartLineNumber = currentLineNumber;
|
|
}
|
|
|
|
if (token[0] === Diff.Diff.Operation.Delete) {
|
|
hasRemoved = true;
|
|
} else {
|
|
currentLineNumber += token[1].length;
|
|
hasAdded = true;
|
|
}
|
|
}
|
|
if (isInsideBlock) {
|
|
flush();
|
|
}
|
|
if (result.length > 1 && result[0].from === 0 && result[1].from === 0) {
|
|
const merged = {type: EditType.Modify, from: 0, to: result[1].to};
|
|
result.splice(0, 2, merged);
|
|
}
|
|
return result;
|
|
|
|
function flush() {
|
|
let type = EditType.Insert;
|
|
let from = blockStartLineNumber;
|
|
let to = currentLineNumber;
|
|
if (hasAdded && hasRemoved) {
|
|
type = EditType.Modify;
|
|
} else if (!hasAdded && hasRemoved && from === 0 && to === 0) {
|
|
type = EditType.Modify;
|
|
to = 1;
|
|
} else if (!hasAdded && hasRemoved) {
|
|
type = EditType.Delete;
|
|
from -= 1;
|
|
}
|
|
result.push({type: type, from: from, to: to});
|
|
isInsideBlock = false;
|
|
hasAdded = false;
|
|
hasRemoved = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @enum {symbol} */
|
|
export const EditType = {
|
|
Insert: Symbol('Insert'),
|
|
Delete: Symbol('Delete'),
|
|
Modify: Symbol('Modify'),
|
|
};
|