Skip to content

Commit

Permalink
Fixes #145872: Emit model events using the view model outgoing queue
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdima committed Mar 23, 2022
1 parent 188f64e commit 00b43ac
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 34 deletions.
7 changes: 6 additions & 1 deletion src/vs/editor/browser/editorBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, ICursorStateComputer, PositionAffinity } from 'vs/editor/common/model';
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/textModelEvents';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';
import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IEditorWhitespace, IViewModel } from 'vs/editor/common/viewModel';
Expand Down Expand Up @@ -531,6 +531,11 @@ export interface ICodeEditor extends editorCommon.IEditor {
* @event
*/
readonly onDidChangeModelDecorations: Event<IModelDecorationsChangedEvent>;
/**
* An event emitted when the tokens of the current model have changed.
* @internal
*/
readonly onDidChangeModelTokens: Event<IModelTokensChangedEvent>;
/**
* An event emitted when the text inside this editor gained focus (i.e. cursor starts blinking).
* @event
Expand Down
32 changes: 23 additions & 9 deletions src/vs/editor/browser/widget/codeEditorWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { EndOfLinePreference, IIdentifiedSingleEditOperation, IModelDecoration,
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
import { ClassName } from 'vs/editor/common/model/intervalTree';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent } from 'vs/editor/common/textModelEvents';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';
import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/core/editorColorRegistry';
import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground, editorErrorBackground, editorInfoBackground, editorWarningBackground } from 'vs/platform/theme/common/colorRegistry';
import { VerticalRevealType } from 'vs/editor/common/viewEvents';
Expand Down Expand Up @@ -134,6 +134,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
private readonly _onDidChangeModelDecorations: Emitter<IModelDecorationsChangedEvent> = this._register(new Emitter<IModelDecorationsChangedEvent>());
public readonly onDidChangeModelDecorations: Event<IModelDecorationsChangedEvent> = this._onDidChangeModelDecorations.event;

private readonly _onDidChangeModelTokens: Emitter<IModelTokensChangedEvent> = this._register(new Emitter<IModelTokensChangedEvent>());
public readonly onDidChangeModelTokens: Event<IModelTokensChangedEvent> = this._onDidChangeModelTokens.event;

private readonly _onDidChangeConfiguration: Emitter<ConfigurationChangedEvent> = this._register(new Emitter<ConfigurationChangedEvent>());
public readonly onDidChangeConfiguration: Event<ConfigurationChangedEvent> = this._onDidChangeConfiguration.event;

Expand Down Expand Up @@ -1587,14 +1590,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
this._themeService
);

listenersToRemove.push(model.onDidChangeDecorations((e) => this._onDidChangeModelDecorations.fire(e)));
listenersToRemove.push(model.onDidChangeLanguage((e) => {
this._domElement.setAttribute('data-mode-id', model.getLanguageId());
this._onDidChangeModelLanguage.fire(e);
}));
listenersToRemove.push(model.onDidChangeLanguageConfiguration((e) => this._onDidChangeModelLanguageConfiguration.fire(e)));
listenersToRemove.push(model.onDidChangeContent((e) => this._onDidChangeModelContent.fire(e)));
listenersToRemove.push(model.onDidChangeOptions((e) => this._onDidChangeModelOptions.fire(e)));
// Someone might destroy the model from under the editor, so prevent any exceptions by setting a null model
listenersToRemove.push(model.onWillDispose(() => this.setModel(null)));

Expand Down Expand Up @@ -1649,6 +1644,25 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE

break;
}
case OutgoingViewModelEventKind.ModelDecorationsChanged:
this._onDidChangeModelDecorations.fire(e.event);
break;
case OutgoingViewModelEventKind.ModelLanguageChanged:
this._domElement.setAttribute('data-mode-id', model.getLanguageId());
this._onDidChangeModelLanguage.fire(e.event);
break;
case OutgoingViewModelEventKind.ModelLanguageConfigurationChanged:
this._onDidChangeModelLanguageConfiguration.fire(e.event);
break;
case OutgoingViewModelEventKind.ModelContentChanged:
this._onDidChangeModelContent.fire(e.event);
break;
case OutgoingViewModelEventKind.ModelOptionsChanged:
this._onDidChangeModelOptions.fire(e.event);
break;
case OutgoingViewModelEventKind.ModelTokensChanged:
this._onDidChangeModelTokens.fire(e.event);
break;

}
}));
Expand Down
12 changes: 11 additions & 1 deletion src/vs/editor/common/viewModel/viewModelImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { ILineBreaksComputer, ILineBreaksComputerFactory, InjectedText } from 'v
import { ViewEventHandler } from 'vs/editor/common/viewEventHandler';
import { ICoordinatesConverter, IViewModel, IWhitespaceChangeAccessor, MinimapLinesRenderingData, OverviewRulerDecorationsGroup, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel';
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
import { FocusChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent } from 'vs/editor/common/viewModelEventDispatcher';
import { FocusChangedEvent, ModelContentChangedEvent, ModelDecorationsChangedEvent, ModelLanguageChangedEvent, ModelLanguageConfigurationChangedEvent, ModelOptionsChangedEvent, ModelTokensChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent } from 'vs/editor/common/viewModelEventDispatcher';
import { IViewModelLines, ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from 'vs/editor/common/viewModel/viewModelLines';
import { IThemeService } from 'vs/platform/theme/common/themeService';

Expand Down Expand Up @@ -399,6 +399,10 @@ export class ViewModel extends Disposable implements IViewModel {
this._tokenizeViewportSoon.schedule();
}));

this._register(this.model.onDidChangeContent((e) => {
this._eventDispatcher.emitOutgoingEvent(new ModelContentChangedEvent(e));
}));

this._register(this.model.onDidChangeTokens((e) => {
const viewRanges: { fromLineNumber: number; toLineNumber: number }[] = [];
for (let j = 0, lenJ = e.ranges.length; j < lenJ; j++) {
Expand All @@ -415,17 +419,20 @@ export class ViewModel extends Disposable implements IViewModel {
if (e.tokenizationSupportChanged) {
this._tokenizeViewportSoon.schedule();
}
this._eventDispatcher.emitOutgoingEvent(new ModelTokensChangedEvent(e));
}));

this._register(this.model.onDidChangeLanguageConfiguration((e) => {
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewLanguageConfigurationEvent());
this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration, this.languageConfigurationService);
this._cursor.updateConfiguration(this.cursorConfig);
this._eventDispatcher.emitOutgoingEvent(new ModelLanguageConfigurationChangedEvent(e));
}));

this._register(this.model.onDidChangeLanguage((e) => {
this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration, this.languageConfigurationService);
this._cursor.updateConfiguration(this.cursorConfig);
this._eventDispatcher.emitOutgoingEvent(new ModelLanguageChangedEvent(e));
}));

this._register(this.model.onDidChangeOptions((e) => {
Expand All @@ -447,11 +454,14 @@ export class ViewModel extends Disposable implements IViewModel {

this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration, this.languageConfigurationService);
this._cursor.updateConfiguration(this.cursorConfig);

this._eventDispatcher.emitOutgoingEvent(new ModelOptionsChangedEvent(e));
}));

this._register(this.model.onDidChangeDecorations((e) => {
this._decorations.onModelDecorationsChanged();
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
this._eventDispatcher.emitOutgoingEvent(new ModelDecorationsChangedEvent(e));
}));
}

Expand Down
154 changes: 136 additions & 18 deletions src/vs/editor/common/viewModelEventDispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Emitter } from 'vs/base/common/event';
import { Selection } from 'vs/editor/common/core/selection';
import { Disposable } from 'vs/base/common/lifecycle';
import { CursorChangeReason } from 'vs/editor/common/cursorEvents';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';

export class ViewModelEventDispatcher extends Disposable {

Expand Down Expand Up @@ -40,8 +41,9 @@ export class ViewModelEventDispatcher extends Disposable {

private _addOutgoingEvent(e: OutgoingViewModelEvent): void {
for (let i = 0, len = this._outgoingEvents.length; i < len; i++) {
if (this._outgoingEvents[i].kind === e.kind) {
this._outgoingEvents[i] = this._outgoingEvents[i].merge(e);
const mergeResult = (this._outgoingEvents[i].kind === e.kind ? this._outgoingEvents[i].attemptToMerge(e) : null);
if (mergeResult) {
this._outgoingEvents[i] = mergeResult;
return;
}
}
Expand Down Expand Up @@ -179,6 +181,12 @@ export type OutgoingViewModelEvent = (
| HiddenAreasChangedEvent
| ReadOnlyEditAttemptEvent
| CursorStateChangedEvent
| ModelDecorationsChangedEvent
| ModelLanguageChangedEvent
| ModelLanguageConfigurationChangedEvent
| ModelContentChangedEvent
| ModelOptionsChangedEvent
| ModelTokensChangedEvent
);

export const enum OutgoingViewModelEventKind {
Expand All @@ -189,6 +197,12 @@ export const enum OutgoingViewModelEventKind {
HiddenAreasChanged,
ReadOnlyEditAttempt,
CursorStateChanged,
ModelDecorationsChanged,
ModelLanguageChanged,
ModelLanguageConfigurationChanged,
ModelContentChanged,
ModelOptionsChanged,
ModelTokensChanged,
}

export class ContentSizeChangedEvent implements IContentSizeChangedEvent {
Expand Down Expand Up @@ -216,10 +230,9 @@ export class ContentSizeChangedEvent implements IContentSizeChangedEvent {
return (!this.contentWidthChanged && !this.contentHeightChanged);
}


public merge(other: OutgoingViewModelEvent): ContentSizeChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.ContentSizeChanged) {
return this;
public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
if (other.kind !== this.kind) {
return null;
}
return new ContentSizeChangedEvent(this._oldContentWidth, this._oldContentHeight, other.contentWidth, other.contentHeight);
}
Expand All @@ -241,9 +254,9 @@ export class FocusChangedEvent {
return (this.oldHasFocus === this.hasFocus);
}

public merge(other: OutgoingViewModelEvent): FocusChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.FocusChanged) {
return this;
public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
if (other.kind !== this.kind) {
return null;
}
return new FocusChangedEvent(this.oldHasFocus, other.hasFocus);
}
Expand Down Expand Up @@ -292,9 +305,9 @@ export class ScrollChangedEvent {
return (!this.scrollWidthChanged && !this.scrollLeftChanged && !this.scrollHeightChanged && !this.scrollTopChanged);
}

public merge(other: OutgoingViewModelEvent): ScrollChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.ScrollChanged) {
return this;
public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
if (other.kind !== this.kind) {
return null;
}
return new ScrollChangedEvent(
this._oldScrollWidth, this._oldScrollLeft, this._oldScrollHeight, this._oldScrollTop,
Expand All @@ -314,7 +327,10 @@ export class ViewZonesChangedEvent {
return false;
}

public merge(other: OutgoingViewModelEvent): ViewZonesChangedEvent {
public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
if (other.kind !== this.kind) {
return null;
}
return this;
}
}
Expand All @@ -330,7 +346,10 @@ export class HiddenAreasChangedEvent {
return false;
}

public merge(other: OutgoingViewModelEvent): HiddenAreasChangedEvent {
public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
if (other.kind !== this.kind) {
return null;
}
return this;
}
}
Expand Down Expand Up @@ -384,9 +403,9 @@ export class CursorStateChangedEvent {
);
}

public merge(other: OutgoingViewModelEvent): CursorStateChangedEvent {
if (other.kind !== OutgoingViewModelEventKind.CursorStateChanged) {
return this;
public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
if (other.kind !== this.kind) {
return null;
}
return new CursorStateChangedEvent(
this.oldSelections, other.selections, this.oldModelVersionId, other.modelVersionId, other.source, other.reason, this.reachedMaxCursorCount || other.reachedMaxCursorCount
Expand All @@ -405,7 +424,106 @@ export class ReadOnlyEditAttemptEvent {
return false;
}

public merge(other: OutgoingViewModelEvent): ReadOnlyEditAttemptEvent {
public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
if (other.kind !== this.kind) {
return null;
}
return this;
}
}

export class ModelDecorationsChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ModelDecorationsChanged;

constructor(
public readonly event: IModelDecorationsChangedEvent
) { }

public isNoOp(): boolean {
return false;
}

public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
return null;
}
}

export class ModelLanguageChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ModelLanguageChanged;

constructor(
public readonly event: IModelLanguageChangedEvent
) { }

public isNoOp(): boolean {
return false;
}

public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
return null;
}
}

export class ModelLanguageConfigurationChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ModelLanguageConfigurationChanged;

constructor(
public readonly event: IModelLanguageConfigurationChangedEvent
) { }

public isNoOp(): boolean {
return false;
}

public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
return null;
}
}

export class ModelContentChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ModelContentChanged;

constructor(
public readonly event: IModelContentChangedEvent
) { }

public isNoOp(): boolean {
return false;
}

public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
return null;
}
}

export class ModelOptionsChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ModelOptionsChanged;

constructor(
public readonly event: IModelOptionsChangedEvent
) { }

public isNoOp(): boolean {
return false;
}

public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
return null;
}
}

export class ModelTokensChangedEvent {
public readonly kind = OutgoingViewModelEventKind.ModelTokensChanged;

constructor(
public readonly event: IModelTokensChangedEvent
) { }

public isNoOp(): boolean {
return false;
}

public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null {
return null;
}
}
10 changes: 5 additions & 5 deletions src/vs/editor/test/browser/testCodeEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,21 +119,21 @@ export interface TestCodeEditorInstantiationOptions extends TestCodeEditorCreati
serviceCollection?: ServiceCollection;
}

export function withTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: IInstantiationService) => void): void {
export function withTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void {
return _withTestCodeEditor(text, options, callback);
}

export async function withAsyncTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: IInstantiationService) => Promise<void>): Promise<void> {
export async function withAsyncTestCodeEditor(text: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void>): Promise<void> {
return _withTestCodeEditor(text, options, callback);
}

function isTextModel(arg: ITextModel | string | string[] | ITextBufferFactory): arg is ITextModel {
return Boolean(arg && (arg as ITextModel).uri);
}

function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: IInstantiationService) => void): void;
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: IInstantiationService) => Promise<void>): Promise<void>;
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: IInstantiationService) => Promise<void> | void): Promise<void> | void {
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void;
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void>): Promise<void>;
function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise<void> | void): Promise<void> | void {
const disposables = new DisposableStore();
const instantiationService = createCodeEditorServices(disposables, options.serviceCollection);
delete options.serviceCollection;
Expand Down
Loading

0 comments on commit 00b43ac

Please sign in to comment.