mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Fixes #39266: Don't run diff for computing edits in case of model update
This commit is contained in:
parent
efc74018f2
commit
814be2fd75
|
@ -24,11 +24,10 @@ import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
|||
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
|
||||
import { IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { ClassName } from 'vs/editor/common/model/intervalTree';
|
||||
import { ISequence, LcsDiff } from 'vs/base/common/diff/diff';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { overviewRulerWarning, overviewRulerError, overviewRulerInfo } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { ITextModel, IModelDeltaDecoration, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, DefaultEndOfLine, ITextModelCreationOptions, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBufferFactory, ITextBuffer } from 'vs/editor/common/model';
|
||||
import { ITextModel, IModelDeltaDecoration, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, DefaultEndOfLine, ITextModelCreationOptions, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBufferFactory, ITextBuffer, EndOfLinePreference } from 'vs/editor/common/model';
|
||||
|
||||
function MODEL_ID(resource: URI): string {
|
||||
return resource.toString();
|
||||
|
@ -393,94 +392,54 @@ export class ModelServiceImpl implements IModelService {
|
|||
);
|
||||
}
|
||||
|
||||
private static _commonPrefix(a: ILineSequence, aLen: number, aDelta: number, b: ILineSequence, bLen: number, bDelta: number): number {
|
||||
const maxResult = Math.min(aLen, bLen);
|
||||
|
||||
let result = 0;
|
||||
for (let i = 0; i < maxResult && a.getLineContent(aDelta + i) === b.getLineContent(bDelta + i); i++) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _commonSuffix(a: ILineSequence, aLen: number, aDelta: number, b: ILineSequence, bLen: number, bDelta: number): number {
|
||||
const maxResult = Math.min(aLen, bLen);
|
||||
|
||||
let result = 0;
|
||||
for (let i = 0; i < maxResult && a.getLineContent(aDelta + aLen - i) === b.getLineContent(bDelta + bLen - i); i++) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute edits to bring `model` to the state of `textSource`.
|
||||
*/
|
||||
public static _computeEdits(model: ITextModel, textBuffer: ITextBuffer): 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 textBuffer.getLineCount();
|
||||
}
|
||||
public getElementHash(index: number): string {
|
||||
return textBuffer.getLineContent(index + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const diffResult = new LcsDiff(modelLineSequence, textSourceLineSequence).ComputeDiff(false);
|
||||
|
||||
let edits: 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;
|
||||
const textBufferLineCount = textBuffer.getLineCount();
|
||||
const commonPrefix = this._commonPrefix(model, modelLineCount, 1, textBuffer, textBufferLineCount, 1);
|
||||
|
||||
let lines: string[] = [];
|
||||
for (let j = 0; j < modifiedLength; j++) {
|
||||
lines[j] = textBuffer.getLineContent(modifiedStart + j + 1);
|
||||
}
|
||||
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);
|
||||
if (modelLineCount === textBufferLineCount && commonPrefix === modelLineCount) {
|
||||
// equality case
|
||||
return [];
|
||||
}
|
||||
|
||||
return edits;
|
||||
const commonSuffix = this._commonSuffix(model, modelLineCount - commonPrefix, commonPrefix, textBuffer, textBufferLineCount - commonPrefix, commonPrefix);
|
||||
|
||||
let oldRange: Range, newRange: Range;
|
||||
if (commonSuffix > 0) {
|
||||
oldRange = new Range(commonPrefix + 1, 1, modelLineCount - commonSuffix + 1, 1);
|
||||
newRange = new Range(commonPrefix + 1, 1, textBufferLineCount - commonSuffix + 1, 1);
|
||||
} else if (commonPrefix > 0) {
|
||||
oldRange = new Range(commonPrefix, model.getLineMaxColumn(commonPrefix), modelLineCount, model.getLineMaxColumn(modelLineCount));
|
||||
newRange = new Range(commonPrefix, 1 + textBuffer.getLineLength(commonPrefix), textBufferLineCount, 1 + textBuffer.getLineLength(textBufferLineCount));
|
||||
} else {
|
||||
oldRange = new Range(1, 1, modelLineCount, model.getLineMaxColumn(modelLineCount));
|
||||
newRange = new Range(1, 1, textBufferLineCount, 1 + textBuffer.getLineLength(textBufferLineCount));
|
||||
}
|
||||
|
||||
return [EditOperation.replace(oldRange, textBuffer.getValueInRange(newRange, EndOfLinePreference.TextDefined))];
|
||||
}
|
||||
|
||||
public createModel(value: string | ITextBufferFactory, modeOrPromise: TPromise<IMode> | IMode, resource: URI): ITextModel {
|
||||
|
@ -582,3 +541,7 @@ export class ModelServiceImpl implements IModelService {
|
|||
this._onModelModeChanged.fire({ model, oldModeId });
|
||||
}
|
||||
}
|
||||
|
||||
export interface ILineSequence {
|
||||
getLineContent(lineNumber: number): string;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,32 @@ suite('ModelService', () => {
|
|||
assert.equal(model3.getOptions().defaultEOL, DefaultEndOfLine.LF);
|
||||
});
|
||||
|
||||
test('_computeEdits no change', function () {
|
||||
|
||||
const model = TextModel.createFromString(
|
||||
[
|
||||
'This is line one', //16
|
||||
'and this is line number two', //27
|
||||
'it is followed by #3', //20
|
||||
'and finished with the fourth.', //29
|
||||
].join('\n')
|
||||
);
|
||||
|
||||
const textBuffer = createTextBuffer(
|
||||
[
|
||||
'This is line one', //16
|
||||
'and this is line number two', //27
|
||||
'it is followed by #3', //20
|
||||
'and finished with the fourth.', //29
|
||||
].join('\n'),
|
||||
DefaultEndOfLine.LF
|
||||
);
|
||||
|
||||
const actual = ModelServiceImpl._computeEdits(model, textBuffer);
|
||||
|
||||
assert.deepEqual(actual, []);
|
||||
});
|
||||
|
||||
test('_computeEdits first line changed', function () {
|
||||
|
||||
const model = TextModel.createFromString(
|
||||
|
@ -67,7 +93,7 @@ suite('ModelService', () => {
|
|||
const actual = ModelServiceImpl._computeEdits(model, textBuffer);
|
||||
|
||||
assert.deepEqual(actual, [
|
||||
EditOperation.replace(new Range(1, 1, 1, 17), 'This is line One')
|
||||
EditOperation.replace(new Range(1, 1, 2, 1), 'This is line One\n')
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -121,8 +147,15 @@ suite('ModelService', () => {
|
|||
const actual = ModelServiceImpl._computeEdits(model, textBuffer);
|
||||
|
||||
assert.deepEqual(actual, [
|
||||
EditOperation.replace(new Range(1, 1, 1, 17), 'This is line One'),
|
||||
EditOperation.replace(new Range(3, 1, 3, 21), 'It is followed by #3')
|
||||
EditOperation.replace(
|
||||
new Range(1, 1, 4, 1),
|
||||
[
|
||||
'This is line One',
|
||||
'and this is line number two',
|
||||
'It is followed by #3',
|
||||
''
|
||||
].join('\r\n')
|
||||
)
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -149,7 +182,7 @@ suite('ModelService', () => {
|
|||
const actual = ModelServiceImpl._computeEdits(model, textBuffer);
|
||||
|
||||
assert.deepEqual(actual, [
|
||||
EditOperation.replace(new Range(3, 2, 3, 2), '\n')
|
||||
EditOperation.replace(new Range(3, 2, 3, 2), '\r\n')
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue