Reduce usage of EventEmitter (#38417)

This commit is contained in:
Alex Dima
2017-11-17 16:03:10 +01:00
parent 796d7fef4f
commit d6cc5feffd
9 changed files with 173 additions and 233 deletions
+3 -44
View File
@@ -22,41 +22,26 @@ export interface ListenerCallback {
(value: any): void;
}
export interface IEventEmitter extends IDisposable {
addListener(eventType: string, listener: ListenerCallback): IDisposable;
addOneTimeListener(eventType: string, listener: ListenerCallback): IDisposable;
}
export interface IListenersMap {
[key: string]: ListenerCallback[];
}
export class EventEmitter implements IEventEmitter {
export class EventEmitter implements IDisposable {
protected _listeners: IListenersMap;
private _collectedEvents: EmitterEvent[];
private _deferredCnt: number;
private _allowedEventTypes: { [eventType: string]: boolean; };
constructor(allowedEventTypes: string[] = null) {
constructor() {
this._listeners = {};
this._collectedEvents = [];
this._deferredCnt = 0;
if (allowedEventTypes) {
this._allowedEventTypes = {};
for (let i = 0; i < allowedEventTypes.length; i++) {
this._allowedEventTypes[allowedEventTypes[i]] = true;
}
} else {
this._allowedEventTypes = null;
}
}
public dispose(): void {
this._listeners = {};
this._collectedEvents = [];
this._deferredCnt = 0;
this._allowedEventTypes = null;
}
public addListener(eventType: string, listener: ListenerCallback): IDisposable {
@@ -64,10 +49,6 @@ export class EventEmitter implements IEventEmitter {
throw new Error('Use addBulkListener(listener) to register your listener!');
}
if (this._allowedEventTypes && !this._allowedEventTypes.hasOwnProperty(eventType)) {
throw new Error('This object will never emit this event type!');
}
if (this._listeners.hasOwnProperty(eventType)) {
this._listeners[eventType].push(listener);
} else {
@@ -130,9 +111,6 @@ export class EventEmitter implements IEventEmitter {
}
public emit(eventType: string, data: any = {}): void {
if (this._allowedEventTypes && !this._allowedEventTypes.hasOwnProperty(eventType)) {
throw new Error('Cannot emit this event type because it wasn\'t listed!');
}
// Early return if no listeners would get this
if (!this._listeners.hasOwnProperty(eventType)) {
return;
@@ -159,16 +137,6 @@ export class EventEmitter implements IEventEmitter {
}
}
public deferredEmit<T>(callback: () => T): T {
this.beginDeferredEmit();
let result: T = safeInvokeNoArg<T>(callback);
this.endDeferredEmit();
return result;
}
private _emitCollected(): void {
if (this._collectedEvents.length === 0) {
return;
@@ -198,7 +166,7 @@ export class OrderGuaranteeEventEmitter extends EventEmitter {
private _emitQueue: EmitQueueElement[];
constructor() {
super(null);
super();
this._emitQueue = [];
}
@@ -221,15 +189,6 @@ export class OrderGuaranteeEventEmitter extends EventEmitter {
}
}
function safeInvokeNoArg<T>(func: Function): T {
try {
return func();
} catch (e) {
Errors.onUnexpectedError(e);
}
return undefined;
}
function safeInvoke1Arg(func: Function, arg1: any): any {
try {
return func(arg1);
+1 -4
View File
@@ -20,10 +20,7 @@ import { Selection, ISelection } from 'vs/editor/common/core/selection';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { hash } from 'vs/base/common/hash';
import {
IModelContentChangedEvent, IModelDecorationsChangedEvent,
IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelLanguageConfigurationChangedEvent
} from 'vs/editor/common/model/textModelEvents';
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelLanguageConfigurationChangedEvent } from 'vs/editor/common/model/textModelEvents';
import * as editorOptions from 'vs/editor/common/config/editorOptions';
import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
+24 -22
View File
@@ -16,7 +16,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource';
import { TextModel } from 'vs/editor/common/model/textModel';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { ModelRawContentChangedEvent, TextModelEventType, IModelContentChangedEvent, ModelRawChange, IModelContentChange, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/model/textModelEvents';
export interface IValidatedEditOperation {
sortIndex: number;
@@ -39,11 +39,11 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
return new EditableTextModel(RawTextSource.fromString(text), options, languageIdentifier);
}
public onDidChangeRawContent(listener: (e: textModelEvents.ModelRawContentChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelRawContentChanged2, listener);
public onDidChangeRawContent(listener: (e: ModelRawContentChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(TextModelEventType.ModelRawContentChanged2, listener);
}
public onDidChangeContent(listener: (e: textModelEvents.IModelContentChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelContentChanged, listener);
public onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(TextModelEventType.ModelContentChanged, listener);
}
private _commandManager: EditStack;
@@ -93,8 +93,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
public pushEditOperations(beforeCursorState: Selection[], editOperations: editorCommon.IIdentifiedSingleEditOperation[], cursorStateComputer: editorCommon.ICursorStateComputer): Selection[] {
try {
this._eventEmitter.beginDeferredEmit();
this._onDidChangeDecorations.beginDeferredEmit();
return this._pushEditOperations(beforeCursorState, editOperations, cursorStateComputer);
} finally {
this._onDidChangeDecorations.endDeferredEmit();
this._eventEmitter.endDeferredEmit();
}
}
@@ -276,10 +278,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
public applyEdits(rawOperations: editorCommon.IIdentifiedSingleEditOperation[]): editorCommon.IIdentifiedSingleEditOperation[] {
try {
this._eventEmitter.beginDeferredEmit();
this._acquireDecorationsTracker();
this._onDidChangeDecorations.beginDeferredEmit();
return this._applyEdits(rawOperations);
} finally {
this._releaseDecorationsTracker();
this._onDidChangeDecorations.endDeferredEmit();
this._eventEmitter.endDeferredEmit();
}
}
@@ -472,8 +474,8 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
// Sort operations descending
operations.sort(EditableTextModel._sortOpsDescending);
let rawContentChanges: textModelEvents.ModelRawChange[] = [];
let contentChanges: textModelEvents.IModelContentChange[] = [];
let rawContentChanges: ModelRawChange[] = [];
let contentChanges: IModelContentChange[] = [];
let lineEditsQueue: IIdentifiedLineEdit[] = [];
const queueLineEdit = (lineEdit: IIdentifiedLineEdit) => {
@@ -506,7 +508,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
this._lines[currentLineNumber - 1].applyEdits(lineEditsQueue.slice(currentLineNumberStart, i));
this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text)
new ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text)
);
currentLineNumber = lineNumber;
@@ -517,7 +519,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
this._lines[currentLineNumber - 1].applyEdits(lineEditsQueue.slice(currentLineNumberStart, lineEditsQueue.length));
this._lineStarts.changeValue(currentLineNumber - 1, this._lines[currentLineNumber - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text)
new ModelRawLineChanged(currentLineNumber, this._lines[currentLineNumber - 1].text)
);
lineEditsQueue = [];
@@ -580,11 +582,11 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
this._lineStarts.changeValue(spliceStartLineNumber - 1, this._lines[spliceStartLineNumber - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(spliceStartLineNumber, this._lines[spliceStartLineNumber - 1].text)
new ModelRawLineChanged(spliceStartLineNumber, this._lines[spliceStartLineNumber - 1].text)
);
rawContentChanges.push(
new textModelEvents.ModelRawLinesDeleted(spliceStartLineNumber + 1, spliceStartLineNumber + spliceCnt)
new ModelRawLinesDeleted(spliceStartLineNumber + 1, spliceStartLineNumber + spliceCnt)
);
}
@@ -604,7 +606,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
let leftoverLine = this._lines[spliceLineNumber - 1].split(spliceColumn);
this._lineStarts.changeValue(spliceLineNumber - 1, this._lines[spliceLineNumber - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLineChanged(spliceLineNumber, this._lines[spliceLineNumber - 1].text)
new ModelRawLineChanged(spliceLineNumber, this._lines[spliceLineNumber - 1].text)
);
this._invalidateLine(spliceLineNumber - 1);
@@ -625,7 +627,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
this._lines[startLineNumber + insertingLinesCnt - 1].append(leftoverLine);
this._lineStarts.changeValue(startLineNumber + insertingLinesCnt - 1, this._lines[startLineNumber + insertingLinesCnt - 1].text.length + this._EOL.length);
rawContentChanges.push(
new textModelEvents.ModelRawLinesInserted(spliceLineNumber + 1, startLineNumber + insertingLinesCnt, newLinesContent.join('\n'))
new ModelRawLinesInserted(spliceLineNumber + 1, startLineNumber + insertingLinesCnt, newLinesContent.join('\n'))
);
}
@@ -647,14 +649,14 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
if (rawContentChanges.length !== 0 || contentChanges.length !== 0) {
this._increaseVersionId();
this._emitModelRawContentChangedEvent(new textModelEvents.ModelRawContentChangedEvent(
this._emitModelRawContentChangedEvent(new ModelRawContentChangedEvent(
rawContentChanges,
this.getVersionId(),
this._isUndoing,
this._isRedoing
));
const e: textModelEvents.IModelContentChangedEvent = {
const e: IModelContentChangedEvent = {
changes: contentChanges,
eol: this._EOL,
versionId: this.getVersionId(),
@@ -662,7 +664,7 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
isRedoing: this._isRedoing,
isFlush: false
};
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelContentChanged, e);
this._eventEmitter.emit(TextModelEventType.ModelContentChanged, e);
}
}
@@ -683,10 +685,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
public undo(): Selection[] {
try {
this._eventEmitter.beginDeferredEmit();
this._acquireDecorationsTracker();
this._onDidChangeDecorations.beginDeferredEmit();
return this._undo();
} finally {
this._releaseDecorationsTracker();
this._onDidChangeDecorations.endDeferredEmit();
this._eventEmitter.endDeferredEmit();
}
}
@@ -708,10 +710,10 @@ export class EditableTextModel extends TextModelWithDecorations implements edito
public redo(): Selection[] {
try {
this._eventEmitter.beginDeferredEmit();
this._acquireDecorationsTracker();
this._onDidChangeDecorations.beginDeferredEmit();
return this._redo();
} finally {
this._releaseDecorationsTracker();
this._onDidChangeDecorations.endDeferredEmit();
this._eventEmitter.endDeferredEmit();
}
}
+6 -24
View File
@@ -5,15 +5,12 @@
'use strict';
import URI from 'vs/base/common/uri';
import {
IModel, ITextModelCreationOptions
} from 'vs/editor/common/editorCommon';
import Event, { Emitter } from 'vs/base/common/event';
import { IModel, ITextModelCreationOptions } from 'vs/editor/common/editorCommon';
import { EditableTextModel } from 'vs/editor/common/model/editableTextModel';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IDisposable } from 'vs/base/common/lifecycle';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
// The hierarchy is:
// Model -> EditableTextModel -> TextModelWithDecorations -> TextModelWithTokens -> TextModel
@@ -22,24 +19,9 @@ var MODEL_ID = 0;
export class Model extends EditableTextModel implements IModel {
public onDidChangeDecorations(listener: (e: textModelEvents.IModelDecorationsChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelDecorationsChanged, listener);
}
public onDidChangeOptions(listener: (e: textModelEvents.IModelOptionsChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelOptionsChanged, listener);
}
public onWillDispose(listener: () => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelDispose, listener);
}
public onDidChangeLanguage(listener: (e: textModelEvents.IModelLanguageChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelLanguageChanged, listener);
}
public onDidChangeLanguageConfiguration(listener: (e: textModelEvents.IModelLanguageConfigurationChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelLanguageConfigurationChanged, listener);
}
public onDidChangeTokens(listener: (e: textModelEvents.IModelTokensChangedEvent) => void): IDisposable {
return this._eventEmitter.addListener(textModelEvents.TextModelEventType.ModelTokensChanged, listener);
}
private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
public readonly onWillDispose: Event<void> = this._onWillDispose.event;
public static createFromString(text: string, options: ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier = null, uri: URI = null): Model {
return new Model(RawTextSource.fromString(text), options, languageIdentifier, uri);
}
@@ -71,7 +53,7 @@ export class Model extends EditableTextModel implements IModel {
public dispose(): void {
this._isDisposing = true;
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelDispose);
this._onWillDispose.fire();
super.dispose();
this._isDisposing = false;
}
+19 -11
View File
@@ -6,6 +6,7 @@
import { OrderGuaranteeEventEmitter } from 'vs/base/common/eventEmitter';
import * as strings from 'vs/base/common/strings';
import Event, { Emitter } from 'vs/base/common/event';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { Range, IRange } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
@@ -16,7 +17,8 @@ import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { TextModelSearch, SearchParams } from 'vs/editor/common/model/textModelSearch';
import { TextSource, ITextSource, IRawTextSource, RawTextSource } from 'vs/editor/common/model/textSource';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { TextModelEventType, IModelContentChangedEvent, ModelRawContentChangedEvent, ModelRawFlush, ModelRawEOLChanged, IModelOptionsChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { Disposable } from 'vs/base/common/lifecycle';
const LIMIT_FIND_COUNT = 999;
export const LONG_LINE_BOUNDARY = 10000;
@@ -26,7 +28,7 @@ export interface ITextModelCreationData {
readonly options: editorCommon.TextModelResolvedOptions;
}
export class TextModel implements editorCommon.ITextModel {
export class TextModel extends Disposable implements editorCommon.ITextModel {
private static MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB
private static MODEL_TOKENIZATION_LIMIT = 20 * 1024 * 1024; // 20 MB
private static MANY_MANY_LINES = 300 * 1000; // 300K lines
@@ -70,6 +72,9 @@ export class TextModel implements editorCommon.ITextModel {
};
}
private readonly _onDidChangeOptions: Emitter<IModelOptionsChangedEvent> = this._register(new Emitter<IModelOptionsChangedEvent>());
public readonly onDidChangeOptions: Event<IModelOptionsChangedEvent> = this._onDidChangeOptions.event;
protected readonly _eventEmitter: OrderGuaranteeEventEmitter;
/*protected*/ _lines: IModelLine[];
@@ -92,6 +97,7 @@ export class TextModel implements editorCommon.ITextModel {
protected readonly _isTooLargeForTokenization: boolean;
constructor(rawTextSource: IRawTextSource, creationOptions: editorCommon.ITextModelCreationOptions) {
super();
this._eventEmitter = new OrderGuaranteeEventEmitter();
const textModelData = TextModel.resolveCreationData(rawTextSource, creationOptions);
@@ -162,7 +168,7 @@ export class TextModel implements editorCommon.ITextModel {
let e = this._options.createChangeEvent(newOpts);
this._options = newOpts;
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelOptionsChanged, e);
this._onDidChangeOptions.fire(e);
}
public detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void {
@@ -292,10 +298,12 @@ export class TextModel implements editorCommon.ITextModel {
this._BOM = null;
this._eventEmitter.dispose();
super.dispose();
}
private _emitContentChanged2(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeLength: number, text: string, isUndoing: boolean, isRedoing: boolean, isFlush: boolean): void {
const e: textModelEvents.IModelContentChangedEvent = {
const e: IModelContentChangedEvent = {
changes: [{
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
rangeLength: rangeLength,
@@ -308,7 +316,7 @@ export class TextModel implements editorCommon.ITextModel {
isFlush: isFlush
};
if (!this._isDisposing) {
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelContentChanged, e);
this._eventEmitter.emit(TextModelEventType.ModelContentChanged, e);
}
}
@@ -360,9 +368,9 @@ export class TextModel implements editorCommon.ITextModel {
this._resetValue(newValue);
this._emitModelRawContentChangedEvent(
new textModelEvents.ModelRawContentChangedEvent(
new ModelRawContentChangedEvent(
[
new textModelEvents.ModelRawFlush()
new ModelRawFlush()
],
this._versionId,
false,
@@ -515,9 +523,9 @@ export class TextModel implements editorCommon.ITextModel {
this._onAfterEOLChange();
this._emitModelRawContentChangedEvent(
new textModelEvents.ModelRawContentChangedEvent(
new ModelRawContentChangedEvent(
[
new textModelEvents.ModelRawEOLChanged()
new ModelRawEOLChanged()
],
this._versionId,
false,
@@ -734,12 +742,12 @@ export class TextModel implements editorCommon.ITextModel {
return new Range(1, 1, lineCount, this.getLineMaxColumn(lineCount));
}
protected _emitModelRawContentChangedEvent(e: textModelEvents.ModelRawContentChangedEvent): void {
protected _emitModelRawContentChangedEvent(e: ModelRawContentChangedEvent): void {
if (this._isDisposing) {
// Do not confuse listeners by emitting any event after disposing
return;
}
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelRawContentChanged2, e);
this._eventEmitter.emit(TextModelEventType.ModelRawContentChanged2, e);
}
private _constructLines(textSource: ITextSource): void {
@@ -11,14 +11,8 @@ import { IRange } from 'vs/editor/common/core/range';
* @internal
*/
export const TextModelEventType = {
ModelDispose: 'modelDispose',
ModelTokensChanged: 'modelTokensChanged',
ModelLanguageChanged: 'modelLanguageChanged',
ModelOptionsChanged: 'modelOptionsChanged',
ModelContentChanged: 'contentChanged',
ModelRawContentChanged2: 'rawContentChanged2',
ModelDecorationsChanged: 'decorationsChanged',
ModelLanguageConfigurationChanged: 'modelLanguageConfigurationChanged'
};
/**
@@ -8,14 +8,16 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import * as strings from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
import Event, { Emitter } from 'vs/base/common/event';
import { Range, IRange } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { TextModelWithTokens } from 'vs/editor/common/model/textModelWithTokens';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IntervalNode, IntervalTree, recomputeMaxEnd, getNodeIsInOverviewRuler } from 'vs/editor/common/model/intervalTree';
import { Disposable } from 'vs/base/common/lifecycle';
let _INSTANCE_COUNT = 0;
/**
@@ -36,14 +38,15 @@ function nextInstanceId(): string {
export class TextModelWithDecorations extends TextModelWithTokens implements editorCommon.ITextModelWithDecorations {
protected readonly _onDidChangeDecorations: DidChangeDecorationsEmitter = this._register(new DidChangeDecorationsEmitter());
public readonly onDidChangeDecorations: Event<IModelDecorationsChangedEvent> = this._onDidChangeDecorations.event;
/**
* Used to workaround broken clients that might attempt using a decoration id generated by a different model.
* It is not globally unique in order to limit it to one character.
*/
private readonly _instanceId: string;
private _lastDecorationId: number;
private _currentDecorationsTrackerCnt: number;
private _currentDecorationsTrackerDidChange: boolean;
private _decorations: { [decorationId: string]: IntervalNode; };
private _decorationsTree: DecorationsTrees;
@@ -52,8 +55,6 @@ export class TextModelWithDecorations extends TextModelWithTokens implements edi
this._instanceId = nextInstanceId();
this._lastDecorationId = 0;
this._currentDecorationsTrackerCnt = 0;
this._currentDecorationsTrackerDidChange = false;
this._decorations = Object.create(null);
this._decorationsTree = new DecorationsTrees();
}
@@ -79,24 +80,8 @@ export class TextModelWithDecorations extends TextModelWithTokens implements edi
// --- END TrackedRanges
protected _acquireDecorationsTracker(): void {
if (this._currentDecorationsTrackerCnt === 0) {
this._currentDecorationsTrackerDidChange = false;
}
this._currentDecorationsTrackerCnt++;
}
protected _releaseDecorationsTracker(): void {
this._currentDecorationsTrackerCnt--;
if (this._currentDecorationsTrackerCnt === 0) {
if (this._currentDecorationsTrackerDidChange) {
this._emitModelDecorationsChangedEvent();
}
}
}
protected _adjustDecorationsForEdit(offset: number, length: number, textLength: number, forceMoveMarkers: boolean): void {
this._currentDecorationsTrackerDidChange = true;
this._onDidChangeDecorations.fire();
this._decorationsTree.acceptReplace(offset, length, textLength, forceMoveMarkers);
}
@@ -138,31 +123,29 @@ export class TextModelWithDecorations extends TextModelWithTokens implements edi
this._assertNotDisposed();
try {
this._eventEmitter.beginDeferredEmit();
this._acquireDecorationsTracker();
this._onDidChangeDecorations.beginDeferredEmit();
return this._changeDecorations(ownerId, callback);
} finally {
this._releaseDecorationsTracker();
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
}
private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: editorCommon.IModelDecorationsChangeAccessor) => T): T {
let changeAccessor: editorCommon.IModelDecorationsChangeAccessor = {
addDecoration: (range: IRange, options: editorCommon.IModelDecorationOptions): string => {
this._currentDecorationsTrackerDidChange = true;
this._onDidChangeDecorations.fire();
return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0];
},
changeDecoration: (id: string, newRange: IRange): void => {
this._currentDecorationsTrackerDidChange = true;
this._onDidChangeDecorations.fire();
this._changeDecorationImpl(id, newRange);
},
changeDecorationOptions: (id: string, options: editorCommon.IModelDecorationOptions) => {
this._currentDecorationsTrackerDidChange = true;
this._onDidChangeDecorations.fire();
this._changeDecorationOptionsImpl(id, _normalizeOptions(options));
},
removeDecoration: (id: string): void => {
this._currentDecorationsTrackerDidChange = true;
this._onDidChangeDecorations.fire();
this._deltaDecorationsImpl(ownerId, [id], []);
},
deltaDecorations: (oldDecorations: string[], newDecorations: editorCommon.IModelDeltaDecoration[]): string[] => {
@@ -170,7 +153,7 @@ export class TextModelWithDecorations extends TextModelWithTokens implements edi
// nothing to do
return [];
}
this._currentDecorationsTrackerDidChange = true;
this._onDidChangeDecorations.fire();
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
}
};
@@ -199,13 +182,11 @@ export class TextModelWithDecorations extends TextModelWithTokens implements edi
}
try {
this._eventEmitter.beginDeferredEmit();
this._acquireDecorationsTracker();
this._currentDecorationsTrackerDidChange = true;
this._onDidChangeDecorations.beginDeferredEmit();
this._onDidChangeDecorations.fire();
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
} finally {
this._releaseDecorationsTracker();
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
}
@@ -312,13 +293,6 @@ export class TextModelWithDecorations extends TextModelWithTokens implements edi
return this._ensureNodesHaveRanges(result);
}
private _emitModelDecorationsChangedEvent(): void {
if (!this._isDisposing) {
let e: textModelEvents.IModelDecorationsChangedEvent = {};
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelDecorationsChanged, e);
}
}
private _getDecorationsInRange(filterRange: Range, filterOwnerId: number, filterOutValidation: boolean): IntervalNode[] {
const startOffset = this._lineStarts.getAccumulatedValue(filterRange.startLineNumber - 2) + filterRange.startColumn - 1;
const endOffset = this._lineStarts.getAccumulatedValue(filterRange.endLineNumber - 2) + filterRange.endColumn - 1;
@@ -625,3 +599,36 @@ function _normalizeOptions(options: editorCommon.IModelDecorationOptions): Model
}
return ModelDecorationOptions.createDynamic(options);
}
export class DidChangeDecorationsEmitter extends Disposable {
private readonly _actual: Emitter<IModelDecorationsChangedEvent> = this._register(new Emitter<IModelDecorationsChangedEvent>());
public readonly event: Event<IModelDecorationsChangedEvent> = this._actual.event;
private _deferredCnt: number;
private _shouldFire: boolean;
constructor() {
super();
this._deferredCnt = 0;
this._shouldFire = false;
}
public beginDeferredEmit(): void {
this._deferredCnt++;
}
public endDeferredEmit(): void {
this._deferredCnt--;
if (this._deferredCnt === 0) {
if (this._shouldFire) {
this._shouldFire = false;
this._actual.fire({});
}
}
}
public fire(): void {
this._shouldFire = true;
}
}
@@ -8,6 +8,7 @@ import * as nls from 'vs/nls';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import { StopWatch } from 'vs/base/common/stopwatch';
import Event, { Emitter } from 'vs/base/common/event';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { TextModel } from 'vs/editor/common/model/textModel';
@@ -21,7 +22,7 @@ import { LineTokens, LineToken } from 'vs/editor/common/core/lineTokens';
import { getWordAtText } from 'vs/editor/common/model/wordHelper';
import { TokenizationResult2 } from 'vs/editor/common/core/token';
import { ITextSource, IRawTextSource } from 'vs/editor/common/model/textSource';
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
import { IModelTokensChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { computeIndentLevel } from 'vs/editor/common/model/modelLine';
class ModelTokensChangedEventBuilder {
@@ -49,7 +50,7 @@ class ModelTokensChangedEventBuilder {
}
}
public build(): textModelEvents.IModelTokensChangedEvent {
public build(): IModelTokensChangedEvent {
if (this._ranges.length === 0) {
return null;
}
@@ -63,6 +64,15 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
private static MODE_TOKENIZATION_FAILED_MSG = nls.localize('mode.tokenizationSupportFailed', "The mode has failed while tokenizing the input.");
private readonly _onDidChangeLanguage: Emitter<IModelLanguageChangedEvent> = this._register(new Emitter<IModelLanguageChangedEvent>());
public readonly onDidChangeLanguage: Event<IModelLanguageChangedEvent> = this._onDidChangeLanguage.event;
private readonly _onDidChangeLanguageConfiguration: Emitter<IModelLanguageConfigurationChangedEvent> = this._register(new Emitter<IModelLanguageConfigurationChangedEvent>());
public readonly onDidChangeLanguageConfiguration: Event<IModelLanguageConfigurationChangedEvent> = this._onDidChangeLanguageConfiguration.event;
private readonly _onDidChangeTokens: Emitter<IModelTokensChangedEvent> = this._register(new Emitter<IModelTokensChangedEvent>());
public readonly onDidChangeTokens: Event<IModelTokensChangedEvent> = this._onDidChangeTokens.event;
private _languageIdentifier: LanguageIdentifier;
private _tokenizationListener: IDisposable;
private _tokenizationSupport: ITokenizationSupport;
@@ -100,7 +110,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
this._languageRegistryListener = LanguageConfigurationRegistry.onDidChange((e) => {
if (e.languageIdentifier.id === this._languageIdentifier.id) {
this._emitModelLanguageConfigurationEvent({});
this._onDidChangeLanguageConfiguration.fire({});
}
});
@@ -164,29 +174,19 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
}
}
private _withModelTokensChangedEventBuilder<T>(callback: (eventBuilder: ModelTokensChangedEventBuilder) => T): T {
let eventBuilder = new ModelTokensChangedEventBuilder();
let result = callback(eventBuilder);
if (!this._isDisposing) {
let e = eventBuilder.build();
if (e) {
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelTokensChanged, e);
}
}
return result;
}
public forceTokenization(lineNumber: number): void {
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
throw new Error('Illegal value ' + lineNumber + ' for `lineNumber`');
}
this._withModelTokensChangedEventBuilder((eventBuilder) => {
this._updateTokensUntilLine(eventBuilder, lineNumber);
});
const eventBuilder = new ModelTokensChangedEventBuilder();
this._updateTokensUntilLine(eventBuilder, lineNumber);
const e = eventBuilder.build();
if (e) {
this._onDidChangeTokens.fire(e);
}
}
public isCheapToTokenize(lineNumber: number): boolean {
@@ -226,7 +226,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
return;
}
let e: textModelEvents.IModelLanguageChangedEvent = {
let e: IModelLanguageChangedEvent = {
oldLanguage: this._languageIdentifier.language,
newLanguage: languageIdentifier.language
};
@@ -242,8 +242,8 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
toLineNumber: this.getLineCount()
}]
});
this._emitModelModeChangedEvent(e);
this._emitModelLanguageConfigurationEvent({});
this._onDidChangeLanguage.fire(e);
this._onDidChangeLanguageConfiguration.fire({});
}
public getLanguageIdAtPosition(_lineNumber: number, _column: number): LanguageId {
@@ -289,56 +289,60 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
private _revalidateTokensNow(toLineNumber: number = this._invalidLineStartIndex + 1000000): void {
this._withModelTokensChangedEventBuilder((eventBuilder) => {
const eventBuilder = new ModelTokensChangedEventBuilder();
toLineNumber = Math.min(this._lines.length, toLineNumber);
toLineNumber = Math.min(this._lines.length, toLineNumber);
var MAX_ALLOWED_TIME = 20,
fromLineNumber = this._invalidLineStartIndex + 1,
tokenizedChars = 0,
currentCharsToTokenize = 0,
currentEstimatedTimeToTokenize = 0,
sw = StopWatch.create(false),
elapsedTime: number;
var MAX_ALLOWED_TIME = 20,
fromLineNumber = this._invalidLineStartIndex + 1,
tokenizedChars = 0,
currentCharsToTokenize = 0,
currentEstimatedTimeToTokenize = 0,
sw = StopWatch.create(false),
elapsedTime: number;
// Tokenize at most 1000 lines. Estimate the tokenization speed per character and stop when:
// - MAX_ALLOWED_TIME is reached
// - tokenizing the next line would go above MAX_ALLOWED_TIME
// Tokenize at most 1000 lines. Estimate the tokenization speed per character and stop when:
// - MAX_ALLOWED_TIME is reached
// - tokenizing the next line would go above MAX_ALLOWED_TIME
for (var lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
elapsedTime = sw.elapsed();
if (elapsedTime > MAX_ALLOWED_TIME) {
// Stop if MAX_ALLOWED_TIME is reached
for (var lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
elapsedTime = sw.elapsed();
if (elapsedTime > MAX_ALLOWED_TIME) {
// Stop if MAX_ALLOWED_TIME is reached
toLineNumber = lineNumber - 1;
break;
}
// Compute how many characters will be tokenized for this line
currentCharsToTokenize = this._lines[lineNumber - 1].text.length;
if (tokenizedChars > 0) {
// If we have enough history, estimate how long tokenizing this line would take
currentEstimatedTimeToTokenize = (elapsedTime / tokenizedChars) * currentCharsToTokenize;
if (elapsedTime + currentEstimatedTimeToTokenize > MAX_ALLOWED_TIME) {
// Tokenizing this line will go above MAX_ALLOWED_TIME
toLineNumber = lineNumber - 1;
break;
}
// Compute how many characters will be tokenized for this line
currentCharsToTokenize = this._lines[lineNumber - 1].text.length;
if (tokenizedChars > 0) {
// If we have enough history, estimate how long tokenizing this line would take
currentEstimatedTimeToTokenize = (elapsedTime / tokenizedChars) * currentCharsToTokenize;
if (elapsedTime + currentEstimatedTimeToTokenize > MAX_ALLOWED_TIME) {
// Tokenizing this line will go above MAX_ALLOWED_TIME
toLineNumber = lineNumber - 1;
break;
}
}
this._updateTokensUntilLine(eventBuilder, lineNumber);
tokenizedChars += currentCharsToTokenize;
// Skip the lines that got tokenized
lineNumber = Math.max(lineNumber, this._invalidLineStartIndex + 1);
}
elapsedTime = sw.elapsed();
this._updateTokensUntilLine(eventBuilder, lineNumber);
tokenizedChars += currentCharsToTokenize;
if (this._invalidLineStartIndex < this._lines.length) {
this._beginBackgroundTokenization();
}
});
// Skip the lines that got tokenized
lineNumber = Math.max(lineNumber, this._invalidLineStartIndex + 1);
}
elapsedTime = sw.elapsed();
if (this._invalidLineStartIndex < this._lines.length) {
this._beginBackgroundTokenization();
}
const e = eventBuilder.build();
if (e) {
this._onDidChangeTokens.fire(e);
}
}
private _updateTokensUntilLine(eventBuilder: ModelTokensChangedEventBuilder, lineNumber: number): void {
@@ -403,21 +407,9 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
this._invalidLineStartIndex = Math.max(this._invalidLineStartIndex, endLineIndex + 1);
}
private emitModelTokensChangedEvent(e: textModelEvents.IModelTokensChangedEvent): void {
private emitModelTokensChangedEvent(e: IModelTokensChangedEvent): void {
if (!this._isDisposing) {
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelTokensChanged, e);
}
}
private _emitModelLanguageConfigurationEvent(e: textModelEvents.IModelLanguageConfigurationChangedEvent): void {
if (!this._isDisposing) {
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelLanguageConfigurationChanged, e);
}
}
private _emitModelModeChangedEvent(e: textModelEvents.IModelLanguageChangedEvent): void {
if (!this._isDisposing) {
this._eventEmitter.emit(textModelEvents.TextModelEventType.ModelLanguageChanged, e);
this._onDidChangeTokens.fire(e);
}
}
-1
View File
@@ -71,7 +71,6 @@ export class FindReplaceState implements IDisposable {
private _matchesCount: number;
private _currentMatch: Range;
private _onFindReplaceStateChange: Emitter<FindReplaceStateChangedEvent>;
// private _eventEmitter: EventEmitter;
public get searchString(): string { return this._searchString; }
public get replaceString(): string { return this._replaceString; }