提交 e72d8d15 编写于 作者: A Alex Dima

Fixes #46314: Make sure model change events reach the view models first

上级 4e7a6812
......@@ -1002,6 +1002,13 @@ export interface ITextModel {
*/
redo(): Selection[];
/**
* @deprecated Please use `onDidChangeContent` instead.
* An event emitted when the contents of the model have changed.
* @internal
* @event
*/
onDidChangeRawContentFast(listener: (e: ModelRawContentChangedEvent) => void): IDisposable;
/**
* @deprecated Please use `onDidChangeContent` instead.
* An event emitted when the contents of the model have changed.
......
......@@ -211,11 +211,14 @@ export class TextModel extends Disposable implements model.ITextModel {
public readonly onDidChangeOptions: Event<IModelOptionsChangedEvent> = this._onDidChangeOptions.event;
private readonly _eventEmitter: DidChangeContentEmitter = this._register(new DidChangeContentEmitter());
public onDidChangeRawContentFast(listener: (e: ModelRawContentChangedEvent) => void): IDisposable {
return this._eventEmitter.fastEvent((e: InternalModelContentChangeEvent) => listener(e.rawContentChangedEvent));
}
public onDidChangeRawContent(listener: (e: ModelRawContentChangedEvent) => void): IDisposable {
return this._eventEmitter.event((e: InternalModelContentChangeEvent) => listener(e.rawContentChangedEvent));
return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.rawContentChangedEvent));
}
public onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable {
return this._eventEmitter.event((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent));
return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent));
}
//#endregion
......@@ -2600,8 +2603,13 @@ export class DidChangeDecorationsEmitter extends Disposable {
export class DidChangeContentEmitter extends Disposable {
private readonly _actual: Emitter<InternalModelContentChangeEvent> = this._register(new Emitter<InternalModelContentChangeEvent>());
public readonly event: Event<InternalModelContentChangeEvent> = this._actual.event;
/**
* Both `fastEvent` and `slowEvent` work the same way and contain the same events, but first we invoke `fastEvent` and then `slowEvent`.
*/
private readonly _fastEmitter: Emitter<InternalModelContentChangeEvent> = this._register(new Emitter<InternalModelContentChangeEvent>());
public readonly fastEvent: Event<InternalModelContentChangeEvent> = this._fastEmitter.event;
private readonly _slowEmitter: Emitter<InternalModelContentChangeEvent> = this._register(new Emitter<InternalModelContentChangeEvent>());
public readonly slowEvent: Event<InternalModelContentChangeEvent> = this._slowEmitter.event;
private _deferredCnt: number;
private _deferredEvent: InternalModelContentChangeEvent;
......@@ -2622,7 +2630,8 @@ export class DidChangeContentEmitter extends Disposable {
if (this._deferredEvent !== null) {
const e = this._deferredEvent;
this._deferredEvent = null;
this._actual.fire(e);
this._fastEmitter.fire(e);
this._slowEmitter.fire(e);
}
}
}
......@@ -2636,6 +2645,7 @@ export class DidChangeContentEmitter extends Disposable {
}
return;
}
this._actual.fire(e);
this._fastEmitter.fire(e);
this._slowEmitter.fire(e);
}
}
......@@ -173,7 +173,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
private _registerModelEvents(): void {
this._register(this.model.onDidChangeRawContent((e) => {
this._register(this.model.onDidChangeRawContentFast((e) => {
try {
const eventsCollector = this._beginEmit();
......
......@@ -17,11 +17,14 @@ import { IndentAction, IndentationRule } from 'vs/editor/common/modes/languageCo
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { TestConfiguration } from 'vs/editor/test/common/mocks/testConfiguration';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { LanguageIdentifier, ITokenizationSupport, IState, TokenizationRegistry } from 'vs/editor/common/modes';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { CoreNavigationCommands, CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
let H = Handler;
// --------- utils
......@@ -2048,6 +2051,35 @@ suite('Editor Controller - Regression tests', () => {
model.dispose();
});
test('issue #46314: ViewModel is out of sync with Model!', () => {
const tokenizationSupport: ITokenizationSupport = {
getInitialState: () => NULL_STATE,
tokenize: undefined,
tokenize2: (line: string, state: IState): TokenizationResult2 => {
return new TokenizationResult2(null, state);
}
};
const LANGUAGE_ID = 'modelModeTest1';
const languageRegistration = TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
let model = TextModel.createFromString('Just text', undefined, new LanguageIdentifier(LANGUAGE_ID, 0));
withTestCodeEditor(null, { model: model }, (editor1, cursor1) => {
withTestCodeEditor(null, { model: model }, (editor2, cursor2) => {
editor1.onDidChangeCursorPosition(() => {
model.tokenizeIfCheap(1);
});
model.applyEdits([{ range: new Range(1, 1, 1, 1), text: '-' }]);
});
});
languageRegistration.dispose();
model.dispose();
});
});
suite('Editor Controller - Cursor Configuration', () => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册