mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-14 12:11:43 +01:00
Fixes #46314: Make sure model change events reach the view models first
This commit is contained in:
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user