mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Keep view model in sync with model (#44805)
This commit is contained in:
parent
dda0911086
commit
47497b7c5c
|
@ -1018,12 +1018,12 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||
|
||||
public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer): Selection[] {
|
||||
try {
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
return this._pushEditOperations(beforeCursorState, editOperations, cursorStateComputer);
|
||||
} finally {
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
this._eventEmitter.endDeferredEmit();
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1108,12 +1108,12 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||
|
||||
public applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IIdentifiedSingleEditOperation[] {
|
||||
try {
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
return this._applyEdits(rawOperations);
|
||||
} finally {
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
this._eventEmitter.endDeferredEmit();
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1250,12 +1250,12 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||
|
||||
public undo(): Selection[] {
|
||||
try {
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
return this._undo();
|
||||
} finally {
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
this._eventEmitter.endDeferredEmit();
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1275,12 +1275,12 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||
|
||||
public redo(): Selection[] {
|
||||
try {
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
return this._redo();
|
||||
} finally {
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
this._eventEmitter.endDeferredEmit();
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2469,12 +2469,12 @@ export class DidChangeContentEmitter extends Disposable {
|
|||
public readonly event: Event<InternalModelContentChangeEvent> = this._actual.event;
|
||||
|
||||
private _deferredCnt: number;
|
||||
private _deferredEvents: InternalModelContentChangeEvent[];
|
||||
private _deferredEvent: InternalModelContentChangeEvent;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._deferredCnt = 0;
|
||||
this._deferredEvents = [];
|
||||
this._deferredEvent = null;
|
||||
}
|
||||
|
||||
public beginDeferredEmit(): void {
|
||||
|
@ -2484,15 +2484,21 @@ export class DidChangeContentEmitter extends Disposable {
|
|||
public endDeferredEmit(): void {
|
||||
this._deferredCnt--;
|
||||
if (this._deferredCnt === 0) {
|
||||
while (this._deferredEvents.length > 0) {
|
||||
this._actual.fire(this._deferredEvents.shift());
|
||||
if (this._deferredEvent !== null) {
|
||||
const e = this._deferredEvent;
|
||||
this._deferredEvent = null;
|
||||
this._actual.fire(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fire(e: InternalModelContentChangeEvent): void {
|
||||
if (this._deferredCnt > 0) {
|
||||
this._deferredEvents.push(e);
|
||||
if (this._deferredEvent) {
|
||||
this._deferredEvent = this._deferredEvent.merge(e);
|
||||
} else {
|
||||
this._deferredEvent = e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._actual.fire(e);
|
||||
|
|
|
@ -234,6 +234,14 @@ export class ModelRawContentChangedEvent {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static merge(a: ModelRawContentChangedEvent, b: ModelRawContentChangedEvent): ModelRawContentChangedEvent {
|
||||
const changes = [].concat(a.changes).concat(b.changes);
|
||||
const versionId = b.versionId;
|
||||
const isUndoing = (a.isUndoing || b.isUndoing);
|
||||
const isRedoing = (a.isRedoing || b.isRedoing);
|
||||
return new ModelRawContentChangedEvent(changes, versionId, isUndoing, isRedoing);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -244,4 +252,27 @@ export class InternalModelContentChangeEvent {
|
|||
public readonly rawContentChangedEvent: ModelRawContentChangedEvent,
|
||||
public readonly contentChangedEvent: IModelContentChangedEvent,
|
||||
) { }
|
||||
|
||||
public merge(other: InternalModelContentChangeEvent): InternalModelContentChangeEvent {
|
||||
const rawContentChangedEvent = ModelRawContentChangedEvent.merge(this.rawContentChangedEvent, other.rawContentChangedEvent);
|
||||
const contentChangedEvent = InternalModelContentChangeEvent._mergeChangeEvents(this.contentChangedEvent, other.contentChangedEvent);
|
||||
return new InternalModelContentChangeEvent(rawContentChangedEvent, contentChangedEvent);
|
||||
}
|
||||
|
||||
private static _mergeChangeEvents(a: IModelContentChangedEvent, b: IModelContentChangedEvent): IModelContentChangedEvent {
|
||||
const changes = [].concat(a.changes).concat(b.changes);
|
||||
const eol = b.eol;
|
||||
const versionId = b.versionId;
|
||||
const isUndoing = (a.isUndoing || b.isUndoing);
|
||||
const isRedoing = (a.isRedoing || b.isRedoing);
|
||||
const isFlush = (a.isFlush || b.isFlush);
|
||||
return {
|
||||
changes: changes,
|
||||
eol: eol,
|
||||
versionId: versionId,
|
||||
isUndoing: isUndoing,
|
||||
isRedoing: isRedoing,
|
||||
isFlush: isFlush
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
|
|||
let modelVersion = this.model.getVersionId();
|
||||
if (modelVersion !== this._validModelVersionId) {
|
||||
// This is pretty bad, it means we lost track of the model...
|
||||
this._constructLines(false);
|
||||
throw new Error(`ViewModel is out of sync with Model!`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,40 @@ suite('ViewModel', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('issue #44805: SplitLinesCollection: attempt to access a \'newer\' model', () => {
|
||||
const text = [''];
|
||||
testViewModel(text, {}, (viewModel, model) => {
|
||||
assert.equal(viewModel.getLineCount(), 1);
|
||||
|
||||
model.pushEditOperations([], [{
|
||||
range: new Range(1, 1, 1, 1),
|
||||
text: '\ninsert1'
|
||||
}], () => ([]));
|
||||
|
||||
model.pushEditOperations([], [{
|
||||
range: new Range(1, 1, 1, 1),
|
||||
text: '\ninsert2'
|
||||
}], () => ([]));
|
||||
|
||||
model.pushEditOperations([], [{
|
||||
range: new Range(1, 1, 1, 1),
|
||||
text: '\ninsert3'
|
||||
}], () => ([]));
|
||||
|
||||
let viewLineCount: number[] = [];
|
||||
|
||||
viewLineCount.push(viewModel.getLineCount());
|
||||
viewModel.addEventListener((events) => {
|
||||
// Access the view model
|
||||
viewLineCount.push(viewModel.getLineCount());
|
||||
});
|
||||
model.undo();
|
||||
viewLineCount.push(viewModel.getLineCount());
|
||||
|
||||
assert.deepEqual(viewLineCount, [4, 1, 1, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
function assertGetPlainTextToCopy(text: string[], ranges: Range[], emptySelectionClipboard: boolean, expected: string | string[]): void {
|
||||
testViewModel(text, {}, (viewModel, model) => {
|
||||
let actual = viewModel.getPlainTextToCopy(ranges, emptySelectionClipboard);
|
||||
|
|
Loading…
Reference in a new issue