提交 4300f49e 编写于 作者: R rebornix

lcs diff

上级 7689b04d
......@@ -1312,6 +1312,7 @@ export interface IReadonlyTextBuffer {
getLinesContent(): string[];
getLineContent(lineNumber: number): string;
getLineCharCode(lineNumber: number, index: number): number;
getCharCode(offset: number): number;
getLineLength(lineNumber: number): number;
getLineFirstNonWhitespaceColumn(lineNumber: number): number;
getLineLastNonWhitespaceColumn(lineNumber: number): number;
......
......@@ -626,8 +626,7 @@ export class PieceTreeBase {
return this._lastVisitedLine.value;
}
public getLineCharCode(lineNumber: number, index: number): number {
let nodePos = this.nodeAt2(lineNumber, index + 1);
private _getCharCode(nodePos: NodePosition): number {
if (nodePos.remainder === nodePos.node.piece.length) {
// the char we want to fetch is at the head of next node.
let matchingNode = nodePos.node.next();
......@@ -647,6 +646,11 @@ export class PieceTreeBase {
}
}
public getLineCharCode(lineNumber: number, index: number): number {
let nodePos = this.nodeAt2(lineNumber, index + 1);
return this._getCharCode(nodePos);
}
public getLineLength(lineNumber: number): number {
if (lineNumber === this.getLineCount()) {
let startOffset = this.getOffsetAt(lineNumber, 1);
......@@ -655,6 +659,11 @@ export class PieceTreeBase {
return this.getOffsetAt(lineNumber + 1, 1) - this.getOffsetAt(lineNumber, 1) - this._EOLLength;
}
public getCharCode(offset: number): number {
let nodePos = this.nodeAt(offset);
return this._getCharCode(nodePos);
}
public findMatchesInNode(node: TreeNode, searcher: Searcher, startLineNumber: number, startColumn: number, startCursor: BufferCursor, endCursor: BufferCursor, searchData: SearchData, captureMatches: boolean, limitResultCount: number, resultLen: number, result: FindMatch[]) {
let buffer = this._buffers[node.piece.bufferIndex];
let startOffsetInBuffer = this.offsetInBuffer(node.piece.bufferIndex, node.piece.start);
......
......@@ -178,6 +178,10 @@ export class PieceTreeTextBuffer implements ITextBuffer, IDisposable {
return this._pieceTree.getLineCharCode(lineNumber, index);
}
public getCharCode(offset: number): number {
return this._pieceTree.getCharCode(offset);
}
public getLineLength(lineNumber: number): number {
return this._pieceTree.getLineLength(lineNumber);
}
......
......@@ -10,14 +10,31 @@ import { ISCMService } from 'vs/workbench/contrib/scm/common/scm';
import { createProviderComparer } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator';
import { first } from 'vs/base/common/async';
import { INotebookService } from '../../../common/notebookService';
import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel';
import { diff } from '../../../common/notebookCommon';
import { ISequence, LcsDiff } from 'vs/base/common/diff/diff';
import { NotebookTextModel } from '../../../common/model/notebookTextModel';
class CellSequence implements ISequence {
constructor(readonly textModel: NotebookTextModel) {
}
getElements(): string[] | number[] | Int32Array {
const hashValue = new Int32Array(this.textModel.cells.length);
for (let i = 0; i < this.textModel.cells.length; i++) {
hashValue[i] = this.textModel.cells[i].getHashValue();
}
return hashValue;
}
}
export class SCMController extends Disposable implements INotebookEditorContribution {
static id: string = 'workbench.notebook.findController';
private _lastDecorationId: string[] = [];
private _localDisposable = new DisposableStore();
private _lastVersion = -1;
constructor(
private readonly _notebookEditor: INotebookEditor,
......@@ -51,6 +68,12 @@ export class SCMController extends Disposable implements INotebookEditorContribu
return;
}
if (this._lastVersion >= modifiedDocument.versionId) {
return;
}
this._lastVersion = modifiedDocument.versionId;
const uri = modifiedDocument.uri;
const providers = this._scmService.repositories.map(r => r.provider);
const rootedProviders = providers.filter(p => !!p.rootUri);
......@@ -71,54 +94,36 @@ export class SCMController extends Disposable implements INotebookEditorContribu
return;
}
// naive diff, runCode50
// diff: 3.947998046875ms
// diff: 2.615966796875ms
console.time('diff');
const cellDiffs = diff<NotebookCellTextModel>(originalDocument.cells, modifiedDocument.cells, a => {
for (let i = 0; i < originalDocument.cells.length; i++) {
const modifiedCell = originalDocument.cells[i];
if (modifiedCell.getValue() === a.getValue()) {
return true;
}
}
return false;
}, (a, b) => {
return a.getValue() === b.getValue();
});
console.timeEnd('diff');
const diff = new LcsDiff(new CellSequence(originalDocument), new CellSequence(modifiedDocument));
const diffResult = diff.ComputeDiff(false);
const decorations: INotebookDeltaDecoration[] = [];
cellDiffs.forEach(diff => {
if (diff.deleteCount === 0) {
diffResult.changes.forEach(change => {
if (change.originalLength === 0) {
// doesn't exist in original
// insert
decorations.push(...diff.toInsert.map(cell => ({
handle: cell.handle,
options: { gutterClassName: 'nb-gutter-cell-inserted' }
})));
for (let i = 0; i < change.modifiedLength; i++) {
decorations.push({
handle: modifiedDocument.cells[change.modifiedStart + i].handle,
options: { gutterClassName: 'nb-gutter-cell-inserted' }
});
}
} else {
if (diff.toInsert.length === 0) {
if (change.modifiedLength === 0) {
// diff.deleteCount
// removed from original
} else {
// modification
decorations.push(...diff.toInsert.map(cell => ({
handle: cell.handle,
options: { gutterClassName: 'nb-gutter-cell-changed' }
})));
for (let i = 0; i < change.modifiedLength; i++) {
decorations.push({
handle: modifiedDocument.cells[change.modifiedStart + i].handle,
options: { gutterClassName: 'nb-gutter-cell-changed' }
});
}
}
}
});
this._lastDecorationId = this._notebookEditor.deltaCellDecorations(this._lastDecorationId, decorations);
}
......
......@@ -224,15 +224,17 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
scrolling = false;
}));
this._localDisposableStore.add(this._cellListGutter.onDidScroll(() => {
if (scrolling) {
return;
}
//TODO
if (this._cellListGutter.scrollTop !== this.scrollTop) {
this.scrollTop = this._cellListGutter.scrollTop;
}
}));
// this._localDisposableStore.add(this._cellListGutter.onDidScroll(() => {
// if (scrolling) {
// return;
// }
// if (this._cellListGutter.scrollTop !== this.scrollTop) {
// this.scrollTop = this._cellListGutter.scrollTop;
// }
// }));
}
elementAt(position: number): ICellViewModel | undefined {
......
......@@ -11,6 +11,8 @@ import * as model from 'vs/editor/common/model';
import { Range } from 'vs/editor/common/core/range';
import { Disposable } from 'vs/base/common/lifecycle';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { hash } from 'vs/base/common/hash';
export class NotebookCellTextModel extends Disposable implements ICell {
private _onDidChangeOutputs = new Emitter<NotebookCellOutputsSplice[]>();
......@@ -39,6 +41,7 @@ export class NotebookCellTextModel extends Disposable implements ICell {
set metadata(newMetadata: NotebookCellMetadata | undefined) {
this._metadata = newMetadata;
this._hash = null;
this._onDidChangeMetadata.fire();
}
......@@ -48,6 +51,7 @@ export class NotebookCellTextModel extends Disposable implements ICell {
set language(newLanguage: string) {
this._language = newLanguage;
this._hash = null;
this._onDidChangeLanguage.fire(newLanguage);
}
......@@ -64,12 +68,15 @@ export class NotebookCellTextModel extends Disposable implements ICell {
this._textBuffer = bufferFactory.create(model.DefaultEndOfLine.LF);
this._register(this._textBuffer.onDidChangeContent(() => {
this._hash = null;
this._onDidChangeContent.fire();
}));
return this._textBuffer;
}
private _hash: number | null = null;
constructor(
readonly uri: URI,
......@@ -96,6 +103,15 @@ export class NotebookCellTextModel extends Disposable implements ICell {
}
}
getHashValue(): number {
if (this._hash !== null) {
return this._hash;
}
this._hash = hash([hash(this.getValue()), this._metadata]);
return this._hash;
}
getTextLength(): number {
return this.textBuffer.getLength();
}
......
......@@ -214,6 +214,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._mapping.set(mainCells[i].handle, mainCells[i]);
let dirtyStateListener = mainCells[i].onDidChangeContent(() => {
this.setDirty(true);
this._increaseVersionId();
this._onDidChangeContent.fire();
});
......@@ -387,6 +388,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
let dirtyStateListener = cell.onDidChangeContent(() => {
this._isUntitled = false;
this.setDirty(true);
this._increaseVersionId();
this._onDidChangeContent.fire();
});
......@@ -423,6 +425,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._mapping.set(cells[i].handle, cells[i]);
let dirtyStateListener = cells[i].onDidChangeContent(() => {
this.setDirty(true);
this._increaseVersionId();
this._onDidChangeContent.fire();
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册