monaco-editor-core
Version:
A browser based code editor
356 lines (355 loc) • 11.7 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from '../../base/common/event.js';
import { Disposable } from '../../base/common/lifecycle.js';
export class ViewModelEventDispatcher extends Disposable {
constructor() {
super();
this._onEvent = this._register(new Emitter());
this.onEvent = this._onEvent.event;
this._eventHandlers = [];
this._viewEventQueue = null;
this._isConsumingViewEventQueue = false;
this._collector = null;
this._collectorCnt = 0;
this._outgoingEvents = [];
}
emitOutgoingEvent(e) {
this._addOutgoingEvent(e);
this._emitOutgoingEvents();
}
_addOutgoingEvent(e) {
for (let i = 0, len = this._outgoingEvents.length; i < len; i++) {
const mergeResult = (this._outgoingEvents[i].kind === e.kind ? this._outgoingEvents[i].attemptToMerge(e) : null);
if (mergeResult) {
this._outgoingEvents[i] = mergeResult;
return;
}
}
// not merged
this._outgoingEvents.push(e);
}
_emitOutgoingEvents() {
while (this._outgoingEvents.length > 0) {
if (this._collector || this._isConsumingViewEventQueue) {
// right now collecting or emitting view events, so let's postpone emitting
return;
}
const event = this._outgoingEvents.shift();
if (event.isNoOp()) {
continue;
}
this._onEvent.fire(event);
}
}
addViewEventHandler(eventHandler) {
for (let i = 0, len = this._eventHandlers.length; i < len; i++) {
if (this._eventHandlers[i] === eventHandler) {
console.warn('Detected duplicate listener in ViewEventDispatcher', eventHandler);
}
}
this._eventHandlers.push(eventHandler);
}
removeViewEventHandler(eventHandler) {
for (let i = 0; i < this._eventHandlers.length; i++) {
if (this._eventHandlers[i] === eventHandler) {
this._eventHandlers.splice(i, 1);
break;
}
}
}
beginEmitViewEvents() {
this._collectorCnt++;
if (this._collectorCnt === 1) {
this._collector = new ViewModelEventsCollector();
}
return this._collector;
}
endEmitViewEvents() {
this._collectorCnt--;
if (this._collectorCnt === 0) {
const outgoingEvents = this._collector.outgoingEvents;
const viewEvents = this._collector.viewEvents;
this._collector = null;
for (const outgoingEvent of outgoingEvents) {
this._addOutgoingEvent(outgoingEvent);
}
if (viewEvents.length > 0) {
this._emitMany(viewEvents);
}
}
this._emitOutgoingEvents();
}
emitSingleViewEvent(event) {
try {
const eventsCollector = this.beginEmitViewEvents();
eventsCollector.emitViewEvent(event);
}
finally {
this.endEmitViewEvents();
}
}
_emitMany(events) {
if (this._viewEventQueue) {
this._viewEventQueue = this._viewEventQueue.concat(events);
}
else {
this._viewEventQueue = events;
}
if (!this._isConsumingViewEventQueue) {
this._consumeViewEventQueue();
}
}
_consumeViewEventQueue() {
try {
this._isConsumingViewEventQueue = true;
this._doConsumeQueue();
}
finally {
this._isConsumingViewEventQueue = false;
}
}
_doConsumeQueue() {
while (this._viewEventQueue) {
// Empty event queue, as events might come in while sending these off
const events = this._viewEventQueue;
this._viewEventQueue = null;
// Use a clone of the event handlers list, as they might remove themselves
const eventHandlers = this._eventHandlers.slice(0);
for (const eventHandler of eventHandlers) {
eventHandler.handleEvents(events);
}
}
}
}
export class ViewModelEventsCollector {
constructor() {
this.viewEvents = [];
this.outgoingEvents = [];
}
emitViewEvent(event) {
this.viewEvents.push(event);
}
emitOutgoingEvent(e) {
this.outgoingEvents.push(e);
}
}
export class ContentSizeChangedEvent {
constructor(oldContentWidth, oldContentHeight, contentWidth, contentHeight) {
this.kind = 0 /* OutgoingViewModelEventKind.ContentSizeChanged */;
this._oldContentWidth = oldContentWidth;
this._oldContentHeight = oldContentHeight;
this.contentWidth = contentWidth;
this.contentHeight = contentHeight;
this.contentWidthChanged = (this._oldContentWidth !== this.contentWidth);
this.contentHeightChanged = (this._oldContentHeight !== this.contentHeight);
}
isNoOp() {
return (!this.contentWidthChanged && !this.contentHeightChanged);
}
attemptToMerge(other) {
if (other.kind !== this.kind) {
return null;
}
return new ContentSizeChangedEvent(this._oldContentWidth, this._oldContentHeight, other.contentWidth, other.contentHeight);
}
}
export class FocusChangedEvent {
constructor(oldHasFocus, hasFocus) {
this.kind = 1 /* OutgoingViewModelEventKind.FocusChanged */;
this.oldHasFocus = oldHasFocus;
this.hasFocus = hasFocus;
}
isNoOp() {
return (this.oldHasFocus === this.hasFocus);
}
attemptToMerge(other) {
if (other.kind !== this.kind) {
return null;
}
return new FocusChangedEvent(this.oldHasFocus, other.hasFocus);
}
}
export class ScrollChangedEvent {
constructor(oldScrollWidth, oldScrollLeft, oldScrollHeight, oldScrollTop, scrollWidth, scrollLeft, scrollHeight, scrollTop) {
this.kind = 2 /* OutgoingViewModelEventKind.ScrollChanged */;
this._oldScrollWidth = oldScrollWidth;
this._oldScrollLeft = oldScrollLeft;
this._oldScrollHeight = oldScrollHeight;
this._oldScrollTop = oldScrollTop;
this.scrollWidth = scrollWidth;
this.scrollLeft = scrollLeft;
this.scrollHeight = scrollHeight;
this.scrollTop = scrollTop;
this.scrollWidthChanged = (this._oldScrollWidth !== this.scrollWidth);
this.scrollLeftChanged = (this._oldScrollLeft !== this.scrollLeft);
this.scrollHeightChanged = (this._oldScrollHeight !== this.scrollHeight);
this.scrollTopChanged = (this._oldScrollTop !== this.scrollTop);
}
isNoOp() {
return (!this.scrollWidthChanged && !this.scrollLeftChanged && !this.scrollHeightChanged && !this.scrollTopChanged);
}
attemptToMerge(other) {
if (other.kind !== this.kind) {
return null;
}
return new ScrollChangedEvent(this._oldScrollWidth, this._oldScrollLeft, this._oldScrollHeight, this._oldScrollTop, other.scrollWidth, other.scrollLeft, other.scrollHeight, other.scrollTop);
}
}
export class ViewZonesChangedEvent {
constructor() {
this.kind = 3 /* OutgoingViewModelEventKind.ViewZonesChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
if (other.kind !== this.kind) {
return null;
}
return this;
}
}
export class HiddenAreasChangedEvent {
constructor() {
this.kind = 4 /* OutgoingViewModelEventKind.HiddenAreasChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
if (other.kind !== this.kind) {
return null;
}
return this;
}
}
export class CursorStateChangedEvent {
constructor(oldSelections, selections, oldModelVersionId, modelVersionId, source, reason, reachedMaxCursorCount) {
this.kind = 6 /* OutgoingViewModelEventKind.CursorStateChanged */;
this.oldSelections = oldSelections;
this.selections = selections;
this.oldModelVersionId = oldModelVersionId;
this.modelVersionId = modelVersionId;
this.source = source;
this.reason = reason;
this.reachedMaxCursorCount = reachedMaxCursorCount;
}
static _selectionsAreEqual(a, b) {
if (!a && !b) {
return true;
}
if (!a || !b) {
return false;
}
const aLen = a.length;
const bLen = b.length;
if (aLen !== bLen) {
return false;
}
for (let i = 0; i < aLen; i++) {
if (!a[i].equalsSelection(b[i])) {
return false;
}
}
return true;
}
isNoOp() {
return (CursorStateChangedEvent._selectionsAreEqual(this.oldSelections, this.selections)
&& this.oldModelVersionId === this.modelVersionId);
}
attemptToMerge(other) {
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);
}
}
export class ReadOnlyEditAttemptEvent {
constructor() {
this.kind = 5 /* OutgoingViewModelEventKind.ReadOnlyEditAttempt */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
if (other.kind !== this.kind) {
return null;
}
return this;
}
}
export class ModelDecorationsChangedEvent {
constructor(event) {
this.event = event;
this.kind = 7 /* OutgoingViewModelEventKind.ModelDecorationsChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
return null;
}
}
export class ModelLanguageChangedEvent {
constructor(event) {
this.event = event;
this.kind = 8 /* OutgoingViewModelEventKind.ModelLanguageChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
return null;
}
}
export class ModelLanguageConfigurationChangedEvent {
constructor(event) {
this.event = event;
this.kind = 9 /* OutgoingViewModelEventKind.ModelLanguageConfigurationChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
return null;
}
}
export class ModelContentChangedEvent {
constructor(event) {
this.event = event;
this.kind = 10 /* OutgoingViewModelEventKind.ModelContentChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
return null;
}
}
export class ModelOptionsChangedEvent {
constructor(event) {
this.event = event;
this.kind = 11 /* OutgoingViewModelEventKind.ModelOptionsChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
return null;
}
}
export class ModelTokensChangedEvent {
constructor(event) {
this.event = event;
this.kind = 12 /* OutgoingViewModelEventKind.ModelTokensChanged */;
}
isNoOp() {
return false;
}
attemptToMerge(other) {
return null;
}
}