Skip to content

Commit 9d61e31

Browse files
committed
Fixed all tests and align strict and non-strict mode tests. text and options can be updated without restarting the editor
1 parent 55f205e commit 9d61e31

File tree

7 files changed

+300
-195
lines changed

7 files changed

+300
-195
lines changed

packages/client/src/editorApp/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ export interface EditorAppConfig {
5252
domReadOnly?: boolean;
5353
readOnly?: boolean;
5454
overrideAutomaticLayout?: boolean;
55-
editorOptions?: monaco.editor.IStandaloneEditorConstructionOptions;
56-
diffEditorOptions?: monaco.editor.IStandaloneDiffEditorConstructionOptions;
55+
editorOptions?: monaco.editor.IStandaloneEditorConstructionOptions | monaco.editor.IEditorOptions;
56+
diffEditorOptions?: monaco.editor.IStandaloneDiffEditorConstructionOptions | monaco.editor.IDiffEditorOptions;
5757
languageDef?: {
5858
languageExtensionConfig: monaco.languages.ILanguageExtensionPoint;
5959
monarchLanguage?: monaco.languages.IMonarchLanguage;

packages/client/src/editorApp/editorApp.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,14 @@ export class EditorApp {
166166
}
167167
}
168168

169-
if (this.config.editorOptions?.['semanticHighlighting.enabled'] !== undefined) {
170-
StandaloneServices.get(IConfigurationService).updateValue('editor.semanticHighlighting.enabled',
171-
this.config.editorOptions['semanticHighlighting.enabled'], ConfigurationTarget.USER);
169+
// as monaco.editor.IStandaloneEditorConstructionOptions
170+
const editorOptions = this.config.editorOptions;
171+
if (editorOptions !== undefined) {
172+
const cOpt = editorOptions as monaco.editor.IStandaloneEditorConstructionOptions;
173+
if (cOpt['semanticHighlighting.enabled'] !== undefined) {
174+
StandaloneServices.get(IConfigurationService).updateValue('editor.semanticHighlighting.enabled',
175+
cOpt['semanticHighlighting.enabled'], ConfigurationTarget.USER);
176+
}
172177
}
173178

174179
await this.createEditors(htmlContainer);

packages/client/test/editorApp/editorApp-classic.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ describe('Test Test EditorApp (classic)', () => {
5151
uri: `/workspace/${expect.getState().testPath}.js`
5252
}
5353
});
54-
editorAppConfig!.editorOptions!['semanticHighlighting.enabled'] = false;
54+
(editorAppConfig!.editorOptions as monaco.editor.IStandaloneEditorConstructionOptions)['semanticHighlighting.enabled'] = false;
5555
editorAppConfig.id = 'test-semanticHighlighting-false';
5656

5757
const editorApp = new EditorApp(editorAppConfig);
58-
expect(editorApp.getConfig().editorOptions?.['semanticHighlighting.enabled']).toBeFalsy();
58+
expect((editorApp.getConfig().editorOptions as monaco.editor.IStandaloneEditorConstructionOptions)['semanticHighlighting.enabled']).toBeFalsy();
5959
});
6060

6161
test('editorOptions: semanticHighlighting="configuredByTheme"', () => {
@@ -65,11 +65,11 @@ describe('Test Test EditorApp (classic)', () => {
6565
uri: `/workspace/${expect.getState().testPath}.js`
6666
}
6767
});
68-
editorAppConfig!.editorOptions!['semanticHighlighting.enabled'] = 'configuredByTheme';
68+
(editorAppConfig!.editorOptions as monaco.editor.IStandaloneEditorConstructionOptions)['semanticHighlighting.enabled'] = 'configuredByTheme';
6969
editorAppConfig.id = 'test-semanticHighlighting-theme';
7070

7171
const editorApp = new EditorApp(editorAppConfig);
72-
expect(editorApp.getConfig().editorOptions?.['semanticHighlighting.enabled']).toEqual('configuredByTheme');
72+
expect((editorApp.getConfig().editorOptions as monaco.editor.IStandaloneEditorConstructionOptions)['semanticHighlighting.enabled']).toEqual('configuredByTheme');
7373
});
7474

7575
test('editorOptions: semanticHighlighting=true', () => {
@@ -79,11 +79,11 @@ describe('Test Test EditorApp (classic)', () => {
7979
uri: `/workspace/${expect.getState().testPath}.js`
8080
}
8181
});
82-
editorAppConfig!.editorOptions!['semanticHighlighting.enabled'] = true;
82+
(editorAppConfig!.editorOptions as monaco.editor.IStandaloneEditorConstructionOptions)['semanticHighlighting.enabled'] = true;
8383
editorAppConfig.id = 'test-semanticHighlighting-true';
8484

8585
const editorApp = new EditorApp(editorAppConfig);
86-
expect(editorApp.getConfig().editorOptions?.['semanticHighlighting.enabled']).toBeTruthy();
86+
expect((editorApp.getConfig().editorOptions as monaco.editor.IStandaloneEditorConstructionOptions)['semanticHighlighting.enabled']).toBeTruthy();
8787
});
8888

8989
test('Check default values', async () => {

packages/wrapper-react/src/index.tsx

Lines changed: 102 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +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';
6+
import * as monaco from '@codingame/monaco-vscode-editor-api';
77
import { EditorApp, type EditorAppConfig, type TextContents } from 'monaco-languageclient/editorApp';
88
import { type LanguageClientConfig, LanguageClientManager } from 'monaco-languageclient/lcwrapper';
99
import { getEnhancedMonacoEnvironment, type MonacoVscodeApiConfig, MonacoVscodeApiWrapper } from 'monaco-languageclient/vscodeApiWrapper';
@@ -22,6 +22,7 @@ export type MonacoEditorProps = {
2222
onEditorStartDone?: (editorApp?: EditorApp) => void;
2323
onLanguageClientsStartDone?: (lcsManager?: LanguageClientManager) => void;
2424
onTextChanged?: (textChanges: TextContents) => void;
25+
onConfigProcessed?: (editorApp?: EditorApp) => void;
2526
onError?: (error: Error) => void;
2627
onDisposeEditor?: () => void;
2728
onDisposeLanguageClient?: () => void;
@@ -37,45 +38,47 @@ const haveEditorService = () => {
3738
};
3839

3940
const runQueue: Array<{id: string, func: () => Promise<void>}> = [];
40-
let deferred: Deferred | undefined = new Deferred();
41+
// let deferred: Deferred = new Deferred();
42+
let lock = true;
4143
let intervalId: number | unknown | undefined = undefined;
4244

4345
const addQueue = (id: string, func: () => Promise<void>) => {
44-
debugLogging('=======================');
45-
debugLogging(`Adding to queue: ${id}`);
46-
debugLogging(`QUEUE SIZE before: ${runQueue.length}`);
46+
debugLogging('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
47+
debugLogging(`Adding to queue: ${id}: QUEUE SIZE before: ${runQueue.length}`);
4748
runQueue.push({id, func});
4849

4950
kickQueue();
5051
};
5152

5253
const executeQueue = async () => {
53-
deferred = new Deferred();
54-
while (runQueue.length > 0) {
55-
const queueObj = runQueue.shift();
56-
if (queueObj !== undefined) {
57-
debugLogging('=======================');
58-
debugLogging(`QUEUE ${queueObj.id} SIZE before: ${runQueue.length}`);
59-
debugLogging(`QUEUE ${queueObj.id} start`, true);
60-
await queueObj.func();
61-
debugLogging(`QUEUE ${queueObj.id} SIZE after: ${runQueue.length}`);
62-
debugLogging(`QUEUE ${queueObj.id} end`);
54+
// while (runQueue.length > 0) {
55+
console.log(`Queue size: ${runQueue.length}`);
56+
57+
if (runQueue.length > 0) {
58+
lock = true;
59+
while (runQueue.length > 0) {
60+
// deferred = new Deferred();
61+
const lengthBefore = runQueue.length;
62+
const queueObj = runQueue.shift();
63+
debugLogging('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<');
64+
debugLogging(`QUEUE ${queueObj?.id} start: SIZE before: ${lengthBefore}`, true);
65+
await queueObj?.func();
66+
debugLogging(`QUEUE ${queueObj?.id} end: SIZE after: ${runQueue.length}`);
67+
// deferred.resolve();
6368
}
69+
lock = false;
6470
}
65-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
66-
deferred?.resolve();
67-
deferred = undefined;
68-
stopQueue();
6971
};
7072

7173
const kickQueue = () => {
7274
if (intervalId === undefined && runQueue.length > 0) {
7375
intervalId = setInterval(async () => {
74-
if (deferred !== undefined) {
75-
await deferred.promise;
76-
}
7776
debugLogging('Checking queue...');
78-
executeQueue();
77+
if (!lock) {
78+
// await deferred.promise;
79+
executeQueue();
80+
stopQueue();
81+
}
7982
}, 50);
8083
}
8184
};
@@ -108,19 +111,21 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
108111
onEditorStartDone,
109112
onLanguageClientsStartDone,
110113
onTextChanged,
114+
onConfigProcessed,
111115
onError,
112116
onDisposeEditor,
113117
onDisposeLanguageClient,
114118
modifiedTextValue,
115119
originalTextValue
116120
} = props;
117121

118-
const currentEditorConfig = useRef<EditorAppConfig | undefined>(undefined);
119122
const editorAppRef = useRef<EditorApp>(undefined);
120123
const containerRef = useRef<HTMLDivElement>(null);
121124
const onTextChangedRef = useRef(onTextChanged);
122125
const modifiedCode = useRef<string>(modifiedTextValue);
123126
const originalCode = useRef<string>(originalTextValue);
127+
const launchingRef = useRef<boolean>(false);
128+
const editorAppConfigRef = useRef<EditorAppConfig>(undefined);
124129

125130
const performErrorHandling = (error: Error) => {
126131
if (onError) {
@@ -174,7 +179,8 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
174179
onVscodeApiInitDone?.(apiWrapper);
175180
debugLogging('GLOBAL INIT DONE', true);
176181

177-
deferred?.resolve();
182+
// deferred?.resolve();
183+
lock = false;
178184
} catch (error) {
179185
performErrorHandling(error as Error);
180186
}
@@ -183,69 +189,83 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
183189
}
184190
};
185191

186-
useEffect(() => {
187-
// fast-fail
188-
if (editorAppConfig === undefined) return;
192+
const editorInitFunc = async () => {
193+
try {
194+
debugLogging('INIT', true);
195+
196+
// it is possible to run without an editorApp, for example when using the ViewsService
197+
if (haveEditorService()) {
198+
if (editorAppRef.current === undefined && !launchingRef.current) {
199+
launchingRef.current = true;
200+
debugLogging('INIT: Creating editor', true);
201+
202+
editorAppRef.current = new EditorApp(editorAppConfigRef.current);
203+
if (editorAppRef.current.isStarting() === true || editorAppRef.current.isDisposing() === true) {
204+
await Promise.all([
205+
editorAppRef.current.getStartingAwait(),
206+
editorAppRef.current.getDisposingAwait()
207+
]);
208+
}
189209

190-
// always try to perform global init. Reason: we cannot ensure order
191-
performGlobalInit();
210+
editorAppRef.current.registerOnTextChangedCallback((textChanges) => {
211+
if (textChanges.modified !== undefined) {
212+
modifiedCode.current = textChanges.modified;
213+
}
214+
if (textChanges.original !== undefined) {
215+
originalCode.current = textChanges.original;
216+
}
217+
if (onTextChangedRef.current !== undefined) {
218+
onTextChangedRef.current(textChanges);
219+
}
220+
});
221+
await editorAppRef.current.start(containerRef.current!);
192222

193-
// re-create editor if config changed
194-
const recreateEditor = editorAppRef.current === undefined || currentEditorConfig.current === undefined ||
195-
JSON.stringify(editorAppConfig) !== JSON.stringify(currentEditorConfig.current);
196-
if (recreateEditor) {
197-
const editorInitFunc = async () => {
198-
try {
199-
debugLogging('INIT', true);
223+
onEditorStartDone?.(editorAppRef.current);
224+
launchingRef.current = false;
225+
}
226+
}
200227

201-
// it is possible to run without an editorApp, for example when using the ViewsService
202-
if (haveEditorService()) {
203-
debugLogging('INIT: Creating editor', true);
228+
debugLogging('INIT DONE', true);
229+
} catch (error) {
230+
performErrorHandling(error as Error);
231+
}
232+
};
204233

205-
await handleEditorDispose();
234+
const configProcessedFunc = () => {
235+
if (!launchingRef.current) {
236+
if (editorAppConfigRef.current?.codeResources !== undefined && editorAppRef.current) {
237+
editorAppRef.current.updateCodeResources(editorAppConfigRef.current.codeResources);
238+
}
239+
if (editorAppConfigRef.current?.editorOptions !== undefined && editorAppRef.current) {
240+
if (!editorAppRef.current.isDiffEditor()) {
241+
editorAppRef.current.getEditor()?.updateOptions(editorAppConfigRef.current.editorOptions as monaco.editor.IEditorOptions);
242+
}
243+
}
244+
if (editorAppConfigRef.current?.diffEditorOptions !== undefined && editorAppRef.current) {
245+
if (editorAppRef.current.isDiffEditor()) {
246+
editorAppRef.current.getDiffEditor()?.updateOptions(editorAppConfigRef.current.diffEditorOptions as monaco.editor.IDiffEditorOptions);
247+
}
248+
}
249+
}
250+
onConfigProcessed?.(editorAppRef.current);
251+
debugLogging('Config processed');
252+
};
206253

207-
currentEditorConfig.current = editorAppConfig;
208-
editorAppRef.current = new EditorApp(editorAppConfig);
209-
if (editorAppRef.current.isStarting() === true || editorAppRef.current.isDisposing() === true) {
210-
await Promise.all([
211-
editorAppRef.current.getStartingAwait(),
212-
editorAppRef.current.getDisposingAwait()
213-
]);
214-
}
254+
useEffect(() => {
255+
// fast-fail
256+
if (editorAppConfig === undefined) return;
215257

216-
editorAppRef.current.registerOnTextChangedCallback((textChanges) => {
217-
if (textChanges.modified !== undefined) {
218-
modifiedCode.current = textChanges.modified;
219-
}
220-
if (textChanges.original !== undefined) {
221-
originalCode.current = textChanges.original;
222-
}
223-
if (onTextChangedRef.current !== undefined) {
224-
onTextChangedRef.current(textChanges);
225-
}
226-
});
227-
await editorAppRef.current.start(containerRef.current!);
258+
// always try to perform global init. Reason: we cannot ensure order
259+
performGlobalInit();
228260

229-
onEditorStartDone?.(editorAppRef.current);
230-
}
261+
editorAppConfigRef.current = editorAppConfig;
231262

232-
debugLogging('INIT DONE', true);
233-
} catch (error) {
234-
performErrorHandling(error as Error);
235-
}
236-
};
237-
addQueue('editorInit', editorInitFunc);
263+
addQueue('editorInit', editorInitFunc);
264+
if (editorAppRef.current !== undefined && !launchingRef.current) {
265+
configProcessedFunc();
238266
}
239267
}, [editorAppConfig]);
240268

241-
const handleEditorDispose = async () => {
242-
if (editorAppRef.current !== undefined) {
243-
await editorAppRef.current.dispose();
244-
editorAppRef.current = undefined;
245-
onDisposeEditor?.();
246-
}
247-
};
248-
249269
useEffect(() => {
250270
// fast-fail
251271
if (languageClientConfig === undefined) return;
@@ -282,7 +302,11 @@ export const MonacoEditorReactComp: React.FC<MonacoEditorProps> = (props) => {
282302
// dispose editor if used
283303
debugLogging('DISPOSE', true);
284304

285-
await handleEditorDispose();
305+
if (editorAppRef.current !== undefined) {
306+
await editorAppRef.current.dispose();
307+
editorAppRef.current = undefined;
308+
onDisposeEditor?.();
309+
}
286310

287311
debugLogging('DISPOSE DONE', true);
288312
};

0 commit comments

Comments
 (0)