diff --git a/packages/examples/src/reactTs.tsx b/packages/examples/src/reactTs.tsx
index eaba6fc..6d3e4c3 100644
--- a/packages/examples/src/reactTs.tsx
+++ b/packages/examples/src/reactTs.tsx
@@ -1,5 +1,5 @@
-import React from 'react';
import ReactDOM from 'react-dom/client';
+import React, { useState } from 'react';
import { MonacoEditorReactComp } from '@typefox/monaco-editor-react';
import { UserConfig } from 'monaco-editor-wrapper';
@@ -9,38 +9,55 @@ import 'monaco-editor/esm/vs/language/typescript/monaco.contribution.js';
import { buildWorkerDefinition } from 'monaco-editor-workers';
buildWorkerDefinition('../../../../node_modules/monaco-editor-workers/dist/workers', import.meta.url, false);
-const rootElem = document.getElementById('root')!;
-const userConfig: UserConfig = {
- htmlElement: rootElem,
- wrapperConfig: {
- serviceConfig: {
- enableKeybindingsService: true,
- debugLogging: true
- },
- editorAppConfig: {
- $type: 'classic',
- languageId: 'typescript',
- useDiffEditor: false,
- theme: 'vs-dark',
- code: `function sayHello(): string {
- return "Hello";
-};`
+const EditorDemo: React.FC = () => {
+ const logMessage = 'console.log(\'hello\')';
+ const [content, setContent] = useState(logMessage);
+
+ const rootElem = document.getElementById('root')!;
+ const userConfig: UserConfig = {
+ htmlElement: rootElem,
+ wrapperConfig: {
+ serviceConfig: {
+ enableKeybindingsService: true,
+ debugLogging: true
+ },
+ editorAppConfig: {
+ $type: 'classic',
+ languageId: 'typescript',
+ useDiffEditor: false,
+ theme: 'vs-dark',
+ code: content
+ }
}
- }
-};
+ };
-const onTextChanged = (text: string, isDirty: boolean) => {
- console.log(`Dirty? ${isDirty} Content: ${text}`);
-};
+ const addConsoleMessage = () => {
+ setContent(`${content}\n${logMessage}`);
+ };
-const comp = ;
+ const onTextChanged = (text: string, isDirty: boolean) => {
+ console.log(`Dirty? ${isDirty} Content: ${text}`);
+ };
+ return (
+ <>
+
+
+ >
+
+ );
+};
+
+const comp = ;
+const rootElem = document.getElementById('root')!;
const root = ReactDOM.createRoot(rootElem);
root.render(comp);
diff --git a/packages/examples/src/wrapperTs.ts b/packages/examples/src/wrapperTs.ts
index 97304cd..f2c05e5 100644
--- a/packages/examples/src/wrapperTs.ts
+++ b/packages/examples/src/wrapperTs.ts
@@ -60,13 +60,13 @@ try {
if (wrapper.getMonacoEditorApp()?.getConfig().codeUri === codeUri) {
updateModel({
code: codeOriginal,
- uri: codeOriginalUri,
+ codeUri: codeOriginalUri,
languageId: 'typescript',
});
} else {
updateModel({
code: code,
- uri: codeUri,
+ codeUri: codeUri,
languageId: 'typescript',
});
}
diff --git a/packages/monaco-editor-react/src/index.tsx b/packages/monaco-editor-react/src/index.tsx
index 23dd17d..9eaa628 100644
--- a/packages/monaco-editor-react/src/index.tsx
+++ b/packages/monaco-editor-react/src/index.tsx
@@ -1,4 +1,4 @@
-import { EditorAppClassic, EditorAppConfigClassic, MonacoEditorLanguageClientWrapper, UserConfig, WorkerConfigDirect, WorkerConfigOptions } from 'monaco-editor-wrapper';
+import { EditorAppClassic, MonacoEditorLanguageClientWrapper, UserConfig, WorkerConfigDirect, WorkerConfigOptions, isAppConfigDifferent } from 'monaco-editor-wrapper';
import { IDisposable } from 'monaco-editor';
import * as vscode from 'vscode';
import React, { CSSProperties } from 'react';
@@ -63,37 +63,23 @@ export class MonacoEditorReactComp extends React.Component {
await this.handleReinit();
} else {
if (wrapper !== null) {
- let restarted = false;
+ const prevConfig = prevProps.userConfig.wrapperConfig.editorAppConfig;
+ const config = userConfig.wrapperConfig.editorAppConfig;
+ const appConfigDifferent = isAppConfigDifferent(prevConfig, config, false, false);
// we need to restart if the editor wrapper config changed
- if (prevProps.userConfig.wrapperConfig.editorAppConfig !==
- userConfig.wrapperConfig.editorAppConfig) {
- restarted = true;
+ if (appConfigDifferent) {
await this.handleReinit();
- }
+ } else {
+ // the function now ensure a model update is only required if something else than the code changed
+ this.wrapper.updateModel(userConfig.wrapperConfig.editorAppConfig);
- if (!restarted) {
- const appConfig = userConfig.wrapperConfig.editorAppConfig;
- if (appConfig.$type === 'classic') {
- const options = (appConfig as EditorAppConfigClassic).editorOptions;
- const prevOptions = (prevProps.userConfig.wrapperConfig.editorAppConfig as EditorAppConfigClassic).editorOptions;
- if (options !== prevOptions && options !== undefined) {
- (wrapper.getMonacoEditorApp() as EditorAppClassic).updateMonacoEditorOptions(options);
+ if (prevConfig.$type === 'classic' && config.$type === 'classic') {
+ if (prevConfig.editorOptions !== config.editorOptions) {
+ (wrapper.getMonacoEditorApp() as EditorAppClassic).updateMonacoEditorOptions(config.editorOptions ?? {});
}
}
-
- const languageId = appConfig.languageId;
- const code = appConfig.code;
- const prevLanguageId = prevProps.userConfig.wrapperConfig.editorAppConfig.languageId;
- const prevCode = prevProps.userConfig.wrapperConfig.editorAppConfig.code;
- if (languageId !== prevLanguageId && code !== prevCode) {
- this.wrapper.updateModel({
- languageId: languageId,
- code: code
- });
- }
}
-
}
}
}
diff --git a/packages/monaco-editor-wrapper/src/editorAppBase.ts b/packages/monaco-editor-wrapper/src/editorAppBase.ts
index 611b730..13df5b9 100644
--- a/packages/monaco-editor-wrapper/src/editorAppBase.ts
+++ b/packages/monaco-editor-wrapper/src/editorAppBase.ts
@@ -1,18 +1,21 @@
import { editor, Uri } from 'monaco-editor';
import { createConfiguredEditor, createConfiguredDiffEditor, createModelReference, ITextFileEditorModel } from 'vscode/monaco';
import { IReference } from 'vscode/service-override/editor';
-import { updateUserConfiguration as vscodeUpdateUserConfiguratio } from 'vscode/service-override/configuration';
-import { ModelUpdate, UserConfig, WrapperConfig } from './wrapper.js';
+import { UserConfig, WrapperConfig } from './wrapper.js';
+import { updateUserConfiguration as vscodeUpdateUserConfiguration } from 'vscode/service-override/configuration';
import { EditorAppConfigClassic } from './editorAppClassic.js';
import { EditorAppConfigVscodeApi } from './editorAppVscodeApi.js';
-export type EditorAppBaseConfig = {
+export type ModelUpdate = {
languageId: string;
- code: string;
+ code?: string;
codeUri?: string;
- useDiffEditor: boolean;
codeOriginal?: string;
codeOriginalUri?: string;
+}
+
+export type EditorAppBaseConfig = ModelUpdate & {
+ useDiffEditor: boolean;
domReadOnly?: boolean;
readOnly?: boolean;
userConfiguration?: UserConfiguration;
@@ -114,8 +117,21 @@ export abstract class EditorAppBase {
return Promise.reject(new Error('You cannot update the editor model, because the regular editor is not configured.'));
}
- this.updateAppConfig(modelUpdate);
- await this.updateEditorModel();
+ const modelUpdateType = isModelUpdateRequired(this.getConfig(), modelUpdate);
+
+ if (modelUpdateType === ModelUpdateType.code) {
+ this.updateAppConfig(modelUpdate);
+ if (this.getConfig().useDiffEditor) {
+ this.diffEditor?.getModifiedEditor().setValue(modelUpdate.code ?? '');
+ this.diffEditor?.getOriginalEditor().setValue(modelUpdate.codeOriginal ?? '');
+ } else {
+ this.editor.setValue(modelUpdate.code ?? '');
+ }
+ } else if (modelUpdateType === ModelUpdateType.model) {
+ this.updateAppConfig(modelUpdate);
+ await this.updateEditorModel();
+ }
+ return Promise.resolve();
}
private async updateEditorModel(): Promise {
@@ -134,9 +150,11 @@ export abstract class EditorAppBase {
if (!this.diffEditor) {
return Promise.reject(new Error('You cannot update the diff editor models, because the diffEditor is not configured.'));
}
-
- this.updateAppConfig(modelUpdate);
- return this.updateDiffEditorModel();
+ if (isModelUpdateRequired(this.getConfig(), modelUpdate)) {
+ this.updateAppConfig(modelUpdate);
+ await this.updateDiffEditorModel();
+ }
+ return Promise.resolve();
}
private async updateDiffEditorModel(): Promise {
@@ -167,25 +185,11 @@ export abstract class EditorAppBase {
private updateAppConfig(modelUpdate: ModelUpdate) {
const config = this.getConfig();
- if (modelUpdate.code !== undefined) {
- config.code = modelUpdate.code;
- }
-
- if (modelUpdate.languageId !== undefined) {
- config.languageId = modelUpdate.languageId;
- }
-
- if (modelUpdate.uri !== undefined) {
- config.codeUri = modelUpdate.uri;
- }
-
- if (modelUpdate.codeOriginal !== undefined) {
- config.codeOriginal = modelUpdate.codeOriginal;
- }
-
- if (modelUpdate.codeOriginalUri !== undefined) {
- config.codeOriginalUri = modelUpdate.codeOriginalUri;
- }
+ config.languageId = modelUpdate.languageId;
+ config.code = modelUpdate.code;
+ config.codeUri = modelUpdate.codeUri;
+ config.codeOriginal = modelUpdate.codeOriginal;
+ config.codeOriginalUri = modelUpdate.codeOriginalUri;
}
getEditorUri(uriType: 'code' | 'codeOriginal') {
@@ -208,7 +212,7 @@ export abstract class EditorAppBase {
async updateUserConfiguration(config: UserConfiguration) {
if (config.json) {
- return vscodeUpdateUserConfiguratio(config.json);
+ return vscodeUpdateUserConfiguration(config.json);
}
return Promise.reject(new Error('Supplied config is undefined'));
}
@@ -223,3 +227,60 @@ export abstract class EditorAppBase {
export const isVscodeApiEditorApp = (wrapperConfig: WrapperConfig) => {
return wrapperConfig.editorAppConfig?.$type === 'vscodeApi';
};
+
+export const isCodeUpdateRequired = (config: EditorAppBaseConfig, modelUpdate: ModelUpdate) => {
+ const updateRequired = (modelUpdate.code !== undefined && modelUpdate.code !== config.code) || modelUpdate.codeOriginal !== config.codeOriginal;
+ return updateRequired ? ModelUpdateType.code : ModelUpdateType.none;
+};
+
+export const isModelUpdateRequired = (config: EditorAppBaseConfig, modelUpdate: ModelUpdate): ModelUpdateType => {
+ const codeUpdate = isCodeUpdateRequired(config, modelUpdate);
+
+ type ModelUpdateKeys = keyof typeof modelUpdate;
+ const propsModelUpdate = ['languageId', 'codeUri', 'codeOriginalUri'];
+ const propCompare = (name: string) => {
+ return config[name as ModelUpdateKeys] !== modelUpdate[name as ModelUpdateKeys];
+ };
+ const updateRequired = propsModelUpdate.some(propCompare);
+ return updateRequired ? ModelUpdateType.model : codeUpdate;
+};
+
+export enum ModelUpdateType {
+ none,
+ code,
+ model
+}
+
+export const isAppConfigDifferent = (orgConfig: EditorAppConfigClassic | EditorAppConfigVscodeApi,
+ config: EditorAppConfigClassic | EditorAppConfigVscodeApi, includeModelData: boolean, includeEditorOptions: boolean): boolean => {
+
+ let different = includeModelData ? isModelUpdateRequired(orgConfig, config) !== ModelUpdateType.none : false;
+ if (orgConfig.$type === config.$type) {
+
+ type ClassicKeys = keyof typeof orgConfig;
+ const propsClassic = ['useDiffEditor', 'readOnly', 'domReadOnly', 'userConfiguration', 'automaticLayout', 'languageDef', 'languageExtensionConfig', 'theme', 'themeData'];
+ const propsClassicEditorOptions = ['editorOptions', 'diffEditorOptions'];
+
+ const propCompareClassic = (name: string) => {
+ return orgConfig[name as ClassicKeys] !== config[name as ClassicKeys];
+ };
+
+ const propsVscode = ['useDiffEditor', 'readOnly', 'domReadOnly', 'userConfiguration', 'extension', 'extensionFilesOrContents'];
+ type VscodeApiKeys = keyof typeof orgConfig;
+ const propCompareVscodeApi = (name: string) => {
+ return orgConfig[name as VscodeApiKeys] !== config[name as VscodeApiKeys];
+ };
+
+ if (orgConfig.$type === 'classic' && config.$type === 'classic') {
+ different = different || propsClassic.some(propCompareClassic);
+ if (includeEditorOptions) {
+ different = different || propsClassicEditorOptions.some(propCompareClassic);
+ }
+ } else if (orgConfig.$type === 'vscodeApi' && config.$type === 'vscodeApi') {
+ different = different || propsVscode.some(propCompareVscodeApi);
+ }
+ } else {
+ throw new Error('Provided configurations are not of the same type.');
+ }
+ return different;
+};
diff --git a/packages/monaco-editor-wrapper/src/index.ts b/packages/monaco-editor-wrapper/src/index.ts
index b6d7d95..a0a6377 100644
--- a/packages/monaco-editor-wrapper/src/index.ts
+++ b/packages/monaco-editor-wrapper/src/index.ts
@@ -1,11 +1,16 @@
import {
EditorAppBase,
- isVscodeApiEditorApp
+ isVscodeApiEditorApp,
+ isCodeUpdateRequired,
+ isModelUpdateRequired,
+ isAppConfigDifferent,
+ ModelUpdateType
} from './editorAppBase.js';
import type {
EditorAppBaseConfig,
EditorAppType,
+ ModelUpdate,
UserConfiguration,
} from './editorAppBase.js';
@@ -44,7 +49,6 @@ import {
import type {
UserConfig,
- ModelUpdate,
WrapperConfig
} from './wrapper.js';
@@ -87,6 +91,10 @@ export {
LanguageClientWrapper,
EditorAppBase,
isVscodeApiEditorApp,
+ isCodeUpdateRequired,
+ isModelUpdateRequired,
+ isAppConfigDifferent,
+ ModelUpdateType,
EditorAppClassic,
EditorAppVscodeApi,
Logger
diff --git a/packages/monaco-editor-wrapper/src/wrapper.ts b/packages/monaco-editor-wrapper/src/wrapper.ts
index 6a85004..1e242fc 100644
--- a/packages/monaco-editor-wrapper/src/wrapper.ts
+++ b/packages/monaco-editor-wrapper/src/wrapper.ts
@@ -2,7 +2,7 @@ import { editor } from 'monaco-editor';
import { initServices, wasVscodeApiInitialized, InitializeServiceConfig, MonacoLanguageClient } from 'monaco-languageclient';
import { EditorAppVscodeApi, EditorAppConfigVscodeApi } from './editorAppVscodeApi.js';
import { EditorAppClassic, EditorAppConfigClassic } from './editorAppClassic.js';
-import { UserConfiguration, isVscodeApiEditorApp } from './editorAppBase.js';
+import { ModelUpdate, UserConfiguration, isVscodeApiEditorApp } from './editorAppBase.js';
import { LanguageClientConfig, LanguageClientWrapper } from './languageClientWrapper.js';
import { Logger, LoggerConfig } from './logger.js';
@@ -19,14 +19,6 @@ export type UserConfig = {
languageClientConfig?: LanguageClientConfig;
}
-export type ModelUpdate = {
- languageId?: string;
- code?: string;
- uri?: string;
- codeOriginal?: string;
- codeOriginalUri?: string;
-}
-
/**
* This class is responsible for the overall ochestration.
* It inits, start and disposes the editor apps and the language client (if configured) and provides
diff --git a/packages/monaco-editor-wrapper/test/editorAppBase.test.ts b/packages/monaco-editor-wrapper/test/editorAppBase.test.ts
index 50d3bfa..c4e3194 100644
--- a/packages/monaco-editor-wrapper/test/editorAppBase.test.ts
+++ b/packages/monaco-editor-wrapper/test/editorAppBase.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest';
-import { EditorAppClassic, isVscodeApiEditorApp } from 'monaco-editor-wrapper';
-import { createBaseConfig, createWrapperConfig } from './helper.js';
+import { isAppConfigDifferent, isVscodeApiEditorApp, isModelUpdateRequired, EditorAppClassic, ModelUpdateType, EditorAppConfigVscodeApi } from 'monaco-editor-wrapper';
+import { createBaseConfig, createEditorAppConfig, createWrapperConfig } from './helper.js';
describe('Test EditorAppBase', () => {
@@ -36,4 +36,54 @@ describe('Test EditorAppBase', () => {
const app = new EditorAppClassic('config defaults', config);
expect(app.getConfig().userConfiguration?.json).toEqual('{ "editor.semanticHighlighting.enabled": true }');
});
+
+ test('isModelUpdateRequired', () => {
+ const config = createEditorAppConfig('classic');
+ let modelUpdateType = isModelUpdateRequired(config, { languageId: 'typescript', code: '' });
+ expect(modelUpdateType).toBe(ModelUpdateType.none);
+
+ modelUpdateType = isModelUpdateRequired(config, { languageId: 'typescript' });
+ expect(modelUpdateType).toBe(ModelUpdateType.none);
+
+ modelUpdateType = isModelUpdateRequired(config, { languageId: 'typescript', code: 'test' });
+ expect(modelUpdateType).toBe(ModelUpdateType.code);
+
+ modelUpdateType = isModelUpdateRequired(config, { languageId: 'javascript', code: 'test' });
+ expect(modelUpdateType).toBe(ModelUpdateType.model);
+ });
+
+ test('isAppConfigDifferent: classic', () => {
+ const orgConfig = createEditorAppConfig('classic');
+ const config = createEditorAppConfig('classic');
+ expect(isAppConfigDifferent(orgConfig, config, false, false)).toBeFalsy();
+
+ config.code = 'test';
+ expect(isAppConfigDifferent(orgConfig, config, false, false)).toBeFalsy();
+ expect(isAppConfigDifferent(orgConfig, config, true, false)).toBeTruthy();
+
+ config.code = '';
+ config.useDiffEditor = true;
+ expect(isAppConfigDifferent(orgConfig, config, false, false)).toBeTruthy();
+ });
+
+ test('isAppConfigDifferent: vscodeApi', () => {
+ const orgConfig = createEditorAppConfig('vscodeApi') as EditorAppConfigVscodeApi;
+ const config = createEditorAppConfig('vscodeApi') as EditorAppConfigVscodeApi;
+ expect(isAppConfigDifferent(orgConfig, config, false, true)).toBeFalsy();
+
+ config.code = 'test';
+ expect(isAppConfigDifferent(orgConfig, config, true, false)).toBeTruthy();
+
+ config.code = '';
+ config.extension = {
+ name: 'Tester',
+ publisher: 'Tester',
+ version: '1.0.0',
+ engines: {
+ vscode: '*'
+ }
+ };
+ expect(isAppConfigDifferent(orgConfig, config, false, false)).toBeTruthy();
+ });
+
});
diff --git a/packages/monaco-editor-wrapper/test/helper.ts b/packages/monaco-editor-wrapper/test/helper.ts
index 7b552be..6fb42f7 100644
--- a/packages/monaco-editor-wrapper/test/helper.ts
+++ b/packages/monaco-editor-wrapper/test/helper.ts
@@ -15,11 +15,15 @@ export const createBaseConfig = (type: EditorAppType): UserConfig => {
export const createWrapperConfig = (type: EditorAppType) => {
return {
- editorAppConfig: {
- $type: type,
- languageId: 'typescript',
- code: '',
- useDiffEditor: false,
- }
+ editorAppConfig: createEditorAppConfig(type)
+ };
+};
+
+export const createEditorAppConfig = (type: EditorAppType) => {
+ return {
+ $type: type,
+ languageId: 'typescript',
+ code: '',
+ useDiffEditor: false,
};
};