Skip to content
This repository has been archived by the owner on Mar 22, 2024. It is now read-only.

Allow to init and start separately. #59

Merged
merged 8 commits into from
Nov 26, 2023
828 changes: 210 additions & 618 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"watch": "tsc --build tsconfig.build.json --watch --verbose",
"lint": "eslint {**/src/**/*.ts,**/src/**/*.tsx,**/test/**/*.ts,**/test/**/*.tsx}",
"build": "npm run build --workspaces",
"test": "vitest -c vitest.config.ts",
"test": "node --experimental-import-meta-resolve ./node_modules/vitest/vitest.mjs -c vite.config.ts",
"release:prepare": "npm run reset:repo && npm ci && npm run lint && npm run build",
"reset:repo": "git clean -f -X -d",
"start:example:server:json": "npm run start:server:json --workspace packages/examples",
Expand All @@ -33,12 +33,12 @@
"webdriverio": "~8.22.0"
},
"overrides": {
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.5 <1.84.0"
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.7 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.7 <1.84.0"
},
"resolutions": {
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.5 <1.84.0"
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.7 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.7 <1.84.0"
},
"volta": {
"node": "18.18.2",
Expand Down
28 changes: 14 additions & 14 deletions packages/examples/package.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
{
"name": "monaco-editor-wrapper-examples",
"private": true,
"version": "3.4.0-next.0",
"version": "3.4.0-next.6",
"type": "module",
"dependencies": {
"@codingame/monaco-vscode-configuration-service-override": "~1.83.5",
"@codingame/monaco-vscode-editor-service-override": "~1.83.5",
"@codingame/monaco-vscode-json-default-extension": "~1.83.5",
"@codingame/monaco-vscode-keybindings-service-override": "~1.83.5",
"@codingame/monaco-vscode-python-default-extension": "~1.83.5",
"@typefox/monaco-editor-react": "~2.4.0-next.0",
"@codingame/monaco-vscode-configuration-service-override": "~1.83.7",
"@codingame/monaco-vscode-editor-service-override": "~1.83.7",
"@codingame/monaco-vscode-json-default-extension": "~1.83.7",
"@codingame/monaco-vscode-keybindings-service-override": "~1.83.7",
"@codingame/monaco-vscode-python-default-extension": "~1.83.7",
"@typefox/monaco-editor-react": "~2.4.0-next.6",
"http-server": "~14.1.1",
"langium": "~2.1.1",
"langium-statemachine-dsl": "~2.1.0",
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.7 <1.84.0",
"monaco-editor-workers": "~0.44.0",
"monaco-editor-wrapper": "~3.4.0-next.0",
"monaco-languageclient": "~7.0.0",
"monaco-languageclient-examples": "~7.0.0",
"monaco-editor-wrapper": "~3.4.0-next.6",
"monaco-languageclient": "~7.0.2",
"monaco-languageclient-examples": "~7.0.2",
"react": "~18.2.0",
"react-dom": "~18.2.0",
"request-light": "~0.7.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.5 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.7 <1.84.0",
"vscode-uri": "~3.0.8",
"vscode-languageclient": "~9.0.1",
"vscode-languageserver": "~9.0.1",
Expand All @@ -37,8 +37,8 @@
"vscode": "$vscode"
},
"resolutions": {
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.5 <1.84.0"
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.7 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.7 <1.84.0"
},
"volta": {
"node": "18.18.2",
Expand Down
1 change: 1 addition & 0 deletions packages/examples/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const disposeEditor = async (useDiffEditor: boolean) => {
};

const restartEditor = async (userConfig: UserConfig, htmlElement: HTMLElement | null) => {
await wrapper.dispose();
await wrapper.start(userConfig, htmlElement);
logEditorInfo(userConfig);
};
Expand Down
17 changes: 13 additions & 4 deletions packages/examples/src/langium/wrapperLangium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { buildWorkerDefinition } from 'monaco-editor-workers';
buildWorkerDefinition('../../../node_modules/monaco-editor-workers/dist/workers/', new URL('', window.location.href).href, false);

let wrapper: MonacoEditorLanguageClientWrapper | undefined;
let extended = false;

const htmlElement = document.getElementById('monaco-editor-root');
export const run = async () => {
Expand All @@ -26,7 +27,9 @@ export const run = async () => {
export const startLangiumClientExtended = async () => {
try {
if (checkStarted()) return;
disableButton('button-start-classic');
extended = true;
disableButton('button-start-classic', true);
disableButton('button-start-extended', true);
const config = await setupLangiumClientExtended();
wrapper = new MonacoEditorLanguageClientWrapper();
wrapper.start(config, htmlElement);
Expand All @@ -38,7 +41,8 @@ export const startLangiumClientExtended = async () => {
export const startLangiumClientClassic = async () => {
try {
if (checkStarted()) return;
disableButton('button-start-extended');
disableButton('button-start-classic', true);
disableButton('button-start-extended', true);
const config = await setupLangiumClientClassic();
wrapper = new MonacoEditorLanguageClientWrapper();
await wrapper.start(config, htmlElement!);
Expand All @@ -55,10 +59,10 @@ const checkStarted = () => {
return false;
};

const disableButton = (id: string) => {
const disableButton = (id: string, disabled: boolean) => {
const button = document.getElementById(id) as HTMLButtonElement;
if (button !== null) {
button.disabled = true;
button.disabled = disabled;
}
};

Expand All @@ -67,6 +71,11 @@ export const disposeEditor = async () => {
wrapper.reportStatus();
await wrapper.dispose();
wrapper = undefined;
if (extended) {
disableButton('button-start-extended', false);
} else {
disableButton('button-start-classic', false);
}
};

export const loadLangiumWorker = () => {
Expand Down
5 changes: 4 additions & 1 deletion packages/examples/src/wrapperAdvanced.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override';
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js';
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution.js';
import { EditorAppConfigClassic, LanguageClientError, MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper';
import { buildWorkerDefinition } from 'monaco-editor-workers';

Expand Down Expand Up @@ -114,6 +115,7 @@ const sleepOne = (milliseconds: number) => {
setTimeout(async () => {
alert(`Updating editors after ${milliseconds}ms`);

await wrapper42.dispose();
wrapper42Config.languageClientConfig = undefined;
const appConfig42 = wrapper42Config.wrapperConfig.editorAppConfig as EditorAppConfigClassic;
appConfig42.languageId = 'javascript';
Expand All @@ -129,6 +131,7 @@ const sleepOne = (milliseconds: number) => {
codeOriginal: 'text 1234'
});

await wrapper44.dispose();
const appConfig44 = wrapper44Config.wrapperConfig.editorAppConfig as EditorAppConfigClassic;
appConfig44.languageId = 'text/plain';
appConfig44.useDiffEditor = true;
Expand All @@ -152,10 +155,10 @@ const sleepTwo = (milliseconds: number) => {
setTimeout(async () => {
alert(`Updating last editor after ${milliseconds}ms`);

await wrapper44.dispose();
const appConfig44 = wrapper44Config.wrapperConfig.editorAppConfig as EditorAppConfigClassic;
appConfig44.useDiffEditor = false;
appConfig44.theme = 'vs-dark';

await wrapper44.start(wrapper44Config, document.getElementById('monaco-editor-root-44'));
console.log('Restarted wrapper44.');
}, milliseconds);
Expand Down
14 changes: 7 additions & 7 deletions packages/monaco-editor-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typefox/monaco-editor-react",
"version": "2.4.0-next.0",
"version": "2.4.0-next.6",
"license": "MIT",
"description": "React component for Monaco-Editor and Monaco Languageclient",
"keywords": [
Expand Down Expand Up @@ -50,13 +50,13 @@
"npm": "9.9.0"
},
"dependencies": {
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
"monaco-editor-wrapper": "~3.4.0-next.0",
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.7 <1.84.0",
"monaco-editor-wrapper": "~3.4.0-next.6",
"react": "~18.2.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.5 <1.84.0"
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.7 <1.84.0"
},
"peerDependencies": {
"monaco-editor-wrapper": "~3.4.0-next.0"
"monaco-editor-wrapper": "~3.4.0-next.6"
},
"devDependencies": {
"@types/react": "~18.2.37"
Expand All @@ -66,8 +66,8 @@
"vscode": "$vscode"
},
"resolutions": {
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.5 <1.84.0"
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.7 <1.84.0",
"vscode": "npm:@codingame/monaco-vscode-api@>=1.83.7 <1.84.0"
},
"repository": {
"type": "git",
Expand Down
119 changes: 76 additions & 43 deletions packages/monaco-editor-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export class MonacoEditorReactComp<T extends MonacoEditorProps = MonacoEditorPro
private wrapper: MonacoEditorLanguageClientWrapper = new MonacoEditorLanguageClientWrapper();
private containerElement?: HTMLDivElement;
private _subscription: IDisposable | null = null;
private isStarting?: Promise<void>;
private isRestaring?: Promise<void>;
private started: (value: void | PromiseLike<void>) => void;

constructor(props: T) {
super(props);
Expand All @@ -27,12 +28,35 @@ export class MonacoEditorReactComp<T extends MonacoEditorProps = MonacoEditorPro
await this.handleReinit();
}

private async handleReinit() {
protected async handleReinit() {
await this.destroyMonaco();
await this.initMonaco();
await this.startMonaco();
}

override async componentDidUpdate(prevProps: T) {
const { userConfig } = this.props;
const { wrapper } = this;

const mustReInit = this.isReInitRequired(prevProps);

if (mustReInit) {
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);

const config = userConfig.wrapperConfig.editorAppConfig;
const prevConfig = prevProps.userConfig.wrapperConfig.editorAppConfig;
if (prevConfig.$type === 'classic' && config.$type === 'classic') {
if (prevConfig.editorOptions !== config.editorOptions) {
(wrapper.getMonacoEditorApp() as EditorAppClassic).updateMonacoEditorOptions(config.editorOptions ?? {});
}
}
}
}

protected isReInitRequired(prevProps: T) {
const { className, userConfig } = this.props;
const { wrapper } = this;

Expand All @@ -41,8 +65,8 @@ export class MonacoEditorReactComp<T extends MonacoEditorProps = MonacoEditorPro
}

let mustReInit = false;
const prevConfig = prevProps.userConfig.wrapperConfig.editorAppConfig;
const config = userConfig.wrapperConfig.editorAppConfig;
const prevConfig = prevProps.userConfig.wrapperConfig.editorAppConfig;
const prevWorkerOptions = prevProps.userConfig.languageClientConfig?.options;
const currentWorkerOptions = userConfig.languageClientConfig?.options;
const prevIsWorker = (prevWorkerOptions?.$type === 'WorkerDirect');
Expand All @@ -67,76 +91,85 @@ export class MonacoEditorReactComp<T extends MonacoEditorProps = MonacoEditorPro
mustReInit = (wrapper?.getMonacoEditorApp() as EditorAppExtended).isAppConfigDifferent(prevConfig, config, false) === true;
}

if (mustReInit) {
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 (prevConfig.$type === 'classic' && config.$type === 'classic') {
if (prevConfig.editorOptions !== config.editorOptions) {
(wrapper.getMonacoEditorApp() as EditorAppClassic).updateMonacoEditorOptions(config.editorOptions ?? {});
}
}
}
return mustReInit;
}

override componentWillUnmount() {
this.destroyMonaco();
}

private assignRef = (component: HTMLDivElement) => {
protected assignRef = (component: HTMLDivElement) => {
this.containerElement = component;
};

private async destroyMonaco(): Promise<void> {
protected async destroyMonaco(): Promise<void> {
if (this.wrapper) {
await this.isStarting;
if (this.isRestaring) {
await this.isRestaring;
}
try {
await this.wrapper.dispose();
} catch {
// This is fine
// Sometimes the language client throws an error during disposal
// This should not prevent us from continue working
// The language client may throw an error during disposal.
// This should not prevent us from continue working.
}
}
if (this._subscription) {
this._subscription.dispose();
}
}

private async initMonaco() {
protected async initMonaco() {
const {
userConfig
} = this.props;

// block "destroyMonaco" until start is complete
this.isRestaring = new Promise<void>((resolve) => {
this.started = resolve;
});
await this.wrapper.init(userConfig);
}

protected async startMonaco() {
const {
className,
userConfig,
onTextChanged,
onLoad,
} = this.props;

if (this.containerElement) {
this.containerElement.className = className ?? '';

this.isStarting = this.wrapper.start(userConfig, this.containerElement);
await this.isStarting;
await this.wrapper.startNoInit(this.containerElement);
this.started();
this.isRestaring = undefined;
kaisalmen marked this conversation as resolved.
Show resolved Hide resolved

// once awaiting isStarting is done onLoad is called if available
onLoad && onLoad();

if (onTextChanged) {
const model = this.wrapper.getModel();
if (model) {
const verifyModelContent = () => {
const modelText = model.getValue();
onTextChanged(modelText, modelText !== userConfig.wrapperConfig.editorAppConfig.code);
};

this._subscription = model.onDidChangeContent(() => {
verifyModelContent();
});
// do it initially
verifyModelContent();
}
}
onLoad?.();

this.handleOnTextChanged();
}
}

private handleOnTextChanged() {
const {
userConfig,
onTextChanged
} = this.props;
if (!onTextChanged) return;

const model = this.wrapper.getModel();
if (model) {
const verifyModelContent = () => {
const modelText = model.getValue();
onTextChanged(modelText, modelText !== userConfig.wrapperConfig.editorAppConfig.code);
};

this._subscription = model.onDidChangeContent(() => {
verifyModelContent();
});
// do it initially
verifyModelContent();
}
}

Expand Down
Loading