Skip to content

Commit 69faa52

Browse files
committed
Make queue self-invoking
1 parent df2d27d commit 69faa52

File tree

6 files changed

+100
-100
lines changed

6 files changed

+100
-100
lines changed

packages/client/src/common/utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,16 @@ export const encodeStringOrUrlToDataUrl = (input: string | URL) => {
5252
export const delayExecution = (ms: number) => {
5353
return new Promise((resolve) => setTimeout(resolve, ms));
5454
};
55+
56+
export class Deferred<T = void> {
57+
promise: Promise<T>;
58+
resolve: (value: T | PromiseLike<T>) => void;
59+
reject: (reason?: unknown) => void;
60+
61+
constructor() {
62+
this.promise = new Promise<T>((res, rej) => {
63+
this.resolve = res;
64+
this.reject = rej;
65+
});
66+
}
67+
}

packages/wrapper-react/src/index.tsx

Lines changed: 81 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See LICENSE in the package root for license information.
44
* ------------------------------------------------------------------------------------------ */
55

6+
import { Deferred } from 'monaco-languageclient/common';
67
import { EditorApp, type EditorAppConfig, type TextContents } from 'monaco-languageclient/editorApp';
78
import { type LanguageClientConfig, LanguageClientManager } from 'monaco-languageclient/lcwrapper';
89
import { getEnhancedMonacoEnvironment, type MonacoVscodeApiConfig, MonacoVscodeApiWrapper } from 'monaco-languageclient/vscodeApiWrapper';
@@ -36,8 +37,46 @@ const haveEditorService = () => {
3637
};
3738

3839
const runQueue: Array<{id: string, func: () => Promise<void>}> = [];
39-
let queueAwait: Promise<void> | undefined = undefined;
40-
let queueResolve: ((value: void | PromiseLike<void>) => void) | undefined = undefined;
40+
let queueAwait: Deferred | undefined = new Deferred();
41+
42+
const addQueue = (id: string, func: () => Promise<void>) => {
43+
debugLogging('=======================');
44+
debugLogging(`Adding to queue: ${id}`);
45+
debugLogging(`QUEUE SIZE before: ${runQueue.length}`);
46+
runQueue.push({id, func});
47+
48+
executeQueue();
49+
};
50+
51+
const executeQueue = async () => {
52+
if (queueAwait !== undefined) {
53+
await queueAwait.promise;
54+
}
55+
if (runQueue.length > 0) {
56+
queueAwait = new Deferred();
57+
const queueObj = runQueue.shift();
58+
if (queueObj !== undefined) {
59+
debugLogging(`QUEUE ${queueObj.id} SIZE before: ${runQueue.length}`);
60+
debugLogging(`QUEUE ${queueObj.id} start`, true);
61+
await queueObj.func();
62+
debugLogging(`QUEUE ${queueObj.id} SIZE after: ${runQueue.length}`);
63+
debugLogging(`QUEUE ${queueObj.id} end`);
64+
}
65+
66+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
67+
queueAwait?.resolve();
68+
queueAwait = undefined;
69+
executeQueue();
70+
}
71+
};
72+
73+
const debugLogging = (id: string, useTime?: boolean) => {
74+
if (useTime === true) {
75+
apiWrapper?.getLogger().debug(`${id}: ${Date.now()}`);
76+
} else {
77+
apiWrapper?.getLogger().debug(id);
78+
}
79+
};
4180

4281
export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
4382
const {
@@ -65,47 +104,6 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
65104
const modifiedCode = useRef<string>(modifiedTextValue);
66105
const originalCode = useRef<string>(originalTextValue);
67106

68-
const addQueue = (id: string, func: () => Promise<void>) => {
69-
debugLogging(`Adding to queue: ${id}`);
70-
debugLogging(`QUEUE SIZE before: ${runQueue.length}`);
71-
runQueue.push({id, func});
72-
};
73-
74-
const triggerQueue = () => {
75-
setInterval(() => {
76-
if (queueAwait === undefined) {
77-
queueAwait = new Promise<void>((resolve) => {
78-
queueResolve = resolve;
79-
});
80-
executeQueue();
81-
}
82-
}, 50);
83-
};
84-
85-
const executeQueue = async () => {
86-
while (runQueue.length > 0) {
87-
const queueObj = runQueue.shift();
88-
if (queueObj !== undefined) {
89-
debugLogging(`QUEUE ${queueObj.id} SIZE before: ${runQueue.length}`);
90-
debugLogging(`QUEUE ${queueObj.id} start`, true);
91-
await queueObj.func();
92-
debugLogging(`QUEUE ${queueObj.id} SIZE after: ${runQueue.length}`);
93-
debugLogging(`QUEUE ${queueObj.id} end`);
94-
}
95-
}
96-
queueResolve?.();
97-
queueAwait = undefined;
98-
queueResolve = undefined;
99-
};
100-
101-
const debugLogging = (id: string, useTime?: boolean) => {
102-
if (useTime === true) {
103-
apiWrapper?.getLogger().debug(`${id}: ${Date.now()}`);
104-
} else {
105-
apiWrapper?.getLogger().debug(id);
106-
}
107-
};
108-
109107
const performErrorHandling = (error: Error) => {
110108
if (onError) {
111109
onError(error);
@@ -156,15 +154,14 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
156154
lcsManager.setLogger(apiWrapper.getLogger());
157155

158156
onVscodeApiInitDone?.(apiWrapper);
159-
triggerQueue();
160157
debugLogging('GLOBAL INIT DONE', true);
158+
159+
queueAwait?.resolve();
161160
} catch (error) {
162161
performErrorHandling(error as Error);
163162
}
164163
};
165164
globalInitFunc();
166-
} else if (envEnhanced.vscodeApiInitialised === true) {
167-
triggerQueue();
168165
}
169166
};
170167

@@ -178,47 +175,49 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
178175
// re-create editor if config changed
179176
const recreateEditor = editorAppRef.current === undefined || currentEditorConfig.current === undefined ||
180177
JSON.stringify(editorAppConfig) !== JSON.stringify(currentEditorConfig.current);
181-
const editorInitFunc = async () => {
182-
try {
183-
debugLogging('INIT', true);
178+
if (recreateEditor) {
179+
const editorInitFunc = async () => {
180+
try {
181+
debugLogging('INIT', true);
184182

185-
// it is possible to run without an editorApp, for example when using the ViewsService
186-
if (recreateEditor && haveEditorService()) {
187-
debugLogging('INIT: Creating editor', true);
183+
// it is possible to run without an editorApp, for example when using the ViewsService
184+
if (haveEditorService()) {
185+
debugLogging('INIT: Creating editor', true);
188186

189-
await handleEditorDispose();
187+
await handleEditorDispose();
190188

191-
currentEditorConfig.current = editorAppConfig;
192-
editorAppRef.current = new EditorApp(editorAppConfig);
193-
if (editorAppRef.current.isStarting() === true || editorAppRef.current.isDisposing() === true) {
194-
await Promise.all([
195-
editorAppRef.current.getStartingAwait(),
196-
editorAppRef.current.getDisposingAwait()
197-
]);
198-
}
199-
200-
editorAppRef.current.registerOnTextChangedCallback((textChanges) => {
201-
if (textChanges.modified !== undefined) {
202-
modifiedCode.current = textChanges.modified;
203-
}
204-
if (textChanges.original !== undefined) {
205-
originalCode.current = textChanges.original;
206-
}
207-
if (onTextChangedRef.current !== undefined) {
208-
onTextChangedRef.current(textChanges);
189+
currentEditorConfig.current = editorAppConfig;
190+
editorAppRef.current = new EditorApp(editorAppConfig);
191+
if (editorAppRef.current.isStarting() === true || editorAppRef.current.isDisposing() === true) {
192+
await Promise.all([
193+
editorAppRef.current.getStartingAwait(),
194+
editorAppRef.current.getDisposingAwait()
195+
]);
209196
}
210-
});
211-
await editorAppRef.current.start(containerRef.current!);
212197

213-
onEditorStartDone?.(editorAppRef.current);
214-
}
198+
editorAppRef.current.registerOnTextChangedCallback((textChanges) => {
199+
if (textChanges.modified !== undefined) {
200+
modifiedCode.current = textChanges.modified;
201+
}
202+
if (textChanges.original !== undefined) {
203+
originalCode.current = textChanges.original;
204+
}
205+
if (onTextChangedRef.current !== undefined) {
206+
onTextChangedRef.current(textChanges);
207+
}
208+
});
209+
await editorAppRef.current.start(containerRef.current!);
215210

216-
debugLogging('INIT DONE', true);
217-
} catch (error) {
218-
performErrorHandling(error as Error);
219-
}
220-
};
221-
addQueue('editorInit', editorInitFunc);
211+
onEditorStartDone?.(editorAppRef.current);
212+
}
213+
214+
debugLogging('INIT DONE', true);
215+
} catch (error) {
216+
performErrorHandling(error as Error);
217+
}
218+
};
219+
addQueue('editorInit', editorInitFunc);
220+
}
222221
}, [editorAppConfig]);
223222

224223
const handleEditorDispose = async () => {
@@ -238,7 +237,7 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
238237

239238
const lcInitFunc = async () => {
240239
try {
241-
debugLogging('INIT LC2', true);
240+
debugLogging('INIT LC', true);
242241

243242
await lcsManager.setConfig(languageClientConfig);
244243
await lcsManager.start();

packages/wrapper-react/test/index.strictmode.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
import { LogLevel } from '@codingame/monaco-vscode-api';
77
import { render, type RenderResult } from '@testing-library/react';
88
import { MonacoEditorReactComp } from '@typefox/monaco-editor-react';
9-
import { delayExecution } from 'monaco-languageclient/common';
9+
import { delayExecution, Deferred } from 'monaco-languageclient/common';
1010
import type { EditorApp, TextContents } from 'monaco-languageclient/editorApp';
1111
import { type LanguageClientManager } from 'monaco-languageclient/lcwrapper';
1212
import { type MonacoVscodeApiConfig } from 'monaco-languageclient/vscodeApiWrapper';
1313
import React, { StrictMode, useState } from 'react';
1414
import { describe, expect, test } from 'vitest';
15-
import { cleanHtmlBody, createDefaultEditorAppConfig, createDefaultLanguageClientConfig, Deferred, unmountDelayMs } from './support/helper.js';
15+
import { cleanHtmlBody, createDefaultEditorAppConfig, createDefaultLanguageClientConfig, unmountDelayMs } from './support/helper.js';
1616

1717
describe('Test MonacoEditorReactComp', () => {
1818

packages/wrapper-react/test/index.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
import { LogLevel } from '@codingame/monaco-vscode-api';
77
import { render, type RenderResult } from '@testing-library/react';
88
import { MonacoEditorReactComp } from '@typefox/monaco-editor-react';
9-
import { delayExecution } from 'monaco-languageclient/common';
9+
import { delayExecution, Deferred } from 'monaco-languageclient/common';
1010
import type { EditorApp, TextContents } from 'monaco-languageclient/editorApp';
1111
import { type LanguageClientManager } from 'monaco-languageclient/lcwrapper';
1212
import { type MonacoVscodeApiConfig } from 'monaco-languageclient/vscodeApiWrapper';
1313
import React, { useState } from 'react';
1414
import { describe, expect, test } from 'vitest';
15-
import { cleanHtmlBody, createDefaultEditorAppConfig, createDefaultLanguageClientConfig, Deferred, unmountDelayMs } from './support/helper.js';
15+
import { cleanHtmlBody, createDefaultEditorAppConfig, createDefaultLanguageClientConfig, unmountDelayMs } from './support/helper.js';
1616

1717
describe('Test MonacoEditorReactComp', () => {
1818

packages/wrapper-react/test/index.viewsservice.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55

66
import { LogLevel } from '@codingame/monaco-vscode-api';
77
import { render } from '@testing-library/react';
8+
import { Deferred } from 'monaco-languageclient/common';
89
import { MonacoEditorReactComp } from '@typefox/monaco-editor-react';
910
import { type MonacoVscodeApiConfig } from 'monaco-languageclient/vscodeApiWrapper';
1011
import React from 'react';
1112
import { describe, expect, test } from 'vitest';
12-
import { cleanHtmlBody, createDefaultEditorAppConfig, Deferred } from './support/helper.js';
13+
import { cleanHtmlBody, createDefaultEditorAppConfig } from './support/helper.js';
1314

1415
describe('Test MonacoEditorReactComp', () => {
1516

packages/wrapper-react/test/support/helper.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,3 @@ export const cleanHtmlBody = () => {
4747
// manual clean document body
4848
document.body.innerHTML = '';
4949
};
50-
51-
export class Deferred<T = void> {
52-
promise: Promise<T>;
53-
resolve: (value: T | PromiseLike<T>) => void;
54-
reject: (reason?: unknown) => void;
55-
56-
constructor() {
57-
this.promise = new Promise<T>((res, rej) => {
58-
this.resolve = res;
59-
this.reject = rej;
60-
});
61-
}
62-
}

0 commit comments

Comments
 (0)