Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep undo history even when file changes outside Code. #29655

Merged
merged 5 commits into from
Aug 28, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 101 additions & 3 deletions src/vs/editor/common/services/modelServiceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarker, IMarkerService } from 'vs/platform/markers/common/markers';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Model } from 'vs/editor/common/model/model';
import { IMode, LanguageIdentifier } from 'vs/editor/common/modes';
Expand All @@ -23,9 +24,11 @@ import * as platform from 'vs/base/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
import { IRawTextSource, TextSource, RawTextSource } from 'vs/editor/common/model/textSource';
import { IRawTextSource, TextSource, RawTextSource, ITextSource } from 'vs/editor/common/model/textSource';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { ClassName } from 'vs/editor/common/model/textModelWithDecorations';
import { ISequence, LcsDiff } from 'vs/base/common/diff/diff';
import { EditOperation } from 'vs/editor/common/core/editOperation';

function MODEL_ID(resource: URI): string {
return resource.toString();
Expand Down Expand Up @@ -376,8 +379,103 @@ export class ModelServiceImpl implements IModelService {
return;
}

// Otherwise update model
model.setValueFromTextSource(textSource);
// Otherwise find a diff between the values and update model
// TODO: update BOM, EOL
model.pushEditOperations(
[new Selection(1, 1, 1, 1)],
ModelServiceImpl._computeEdits(model, textSource),
(inverseEditOperations: editorCommon.IIdentifiedSingleEditOperation[]) => [new Selection(1, 1, 1, 1)]
);
}

/**
* Compute edits to bring `model` to the state of `textSource`.
*/
public static _computeEdits(model: editorCommon.IModel, textSource: ITextSource): editorCommon.IIdentifiedSingleEditOperation[] {
const modelLineSequence = new class implements ISequence {
public getLength(): number {
return model.getLineCount();
}
public getElementHash(index: number): string {
return model.getLineContent(index + 1);
}
};
const textSourceLineSequence = new class implements ISequence {
public getLength(): number {
return textSource.lines.length;
}
public getElementHash(index: number): string {
return textSource.lines[index];
}
};

const diffResult = new LcsDiff(modelLineSequence, textSourceLineSequence).ComputeDiff(false);

let edits: editorCommon.IIdentifiedSingleEditOperation[] = [], editsLen = 0;
const modelLineCount = model.getLineCount();
for (let i = 0, len = diffResult.length; i < len; i++) {
const diff = diffResult[i];
const originalStart = diff.originalStart;
const originalLength = diff.originalLength;
const modifiedStart = diff.modifiedStart;
const modifiedLength = diff.modifiedLength;

let lines: string[] = [];
for (let j = 0; j < modifiedLength; j++) {
lines[j] = textSource.lines[modifiedStart + j];
}
let text = lines.join('\n');

let range: Range;
if (originalLength === 0) {
// insertion

if (originalStart === modelLineCount) {
// insert at the end
const maxLineColumn = model.getLineMaxColumn(modelLineCount);
range = new Range(
modelLineCount, maxLineColumn,
modelLineCount, maxLineColumn
);
text = '\n' + text;
} else {
// insert
range = new Range(
originalStart + 1, 1,
originalStart + 1, 1
);
text = text + '\n';
}

} else if (modifiedLength === 0) {
// deletion

if (originalStart + originalLength >= modelLineCount) {
// delete at the end
range = new Range(
originalStart, model.getLineMaxColumn(originalStart),
originalStart + originalLength, model.getLineMaxColumn(originalStart + originalLength)
);
} else {
// delete
range = new Range(
originalStart + 1, 1,
originalStart + originalLength + 1, 1
);
}

} else {
// modification
range = new Range(
originalStart + 1, 1,
originalStart + originalLength, model.getLineMaxColumn(originalStart + originalLength)
);
}

edits[editsLen++] = EditOperation.replace(range, text);
}

return edits;
}

public createModel(value: string | IRawTextSource, modeOrPromise: TPromise<IMode> | IMode, resource: URI): editorCommon.IModel {
Expand Down
Loading