Suppose I have an Angular2 single-page app. In one of the components, I'm using the Monaco editor, which I initialize as follows
(<any>window).require(['vs/editor/editor.main'], () => {
...
monaco.languages.typescript.typescriptDefaults.addExtraLib(response.data, response.url);
...
this._monacoEditor = monaco.editor.create(this._editor.nativeElement, {
value: someData,
language: 'typescript',
});
});
On the "dispose" (ngOnDestroy) of the component, I call:
if (this._monacoEditor) {
this._monacoEditor.dispose();
console.log("Monaco editor disposed");
}
The page works great on first load. When I navigate away, I see the "Monaco editor disposed" message, also as expected. However, when I re-navigate to the page, I get exceptions such as:
//raw.githubusercontent.com/definitelytyped/definitelytyped/master/office-js/office-js.d.ts already a extra lib
Likewise, if I'd had code like "class Foo { }" in the editor, and then navigate away and back, I get an error saying that "Duplicate identifier 'Foo'".
What is the right way to fully dispose of the Monaco editor, and have it be able to start afresh? Or is this a bug?... Thanks!
The call monaco.languages.typescript.typescriptDefaults.addExtraLib is not bound to an editor instance, it affects all editors that use the typescript language until the entire page is destroyed. That call also returns a disposable which you can call (that means that the extra base lib you have added will get removed).
To be clear, these are NOT libraries I'm setting via .addExtraLib. This was also something I had issues with, but it has since been fixed (https://github.com/Microsoft/monaco-editor/issues/112).
The issue here is with code that I write into the editor itself, that somehow sticks around "in memory" even after the Monaco editor is destroyed. That's the thing that I want to clear out.
@Zlatkovsky
If you create models via monaco.editor.createModel, then those models are/remain alive, even when not attached to an editor. The TypeScript language service works in the following way:
In other words:
var editor = monaco.editor.create({ model: null });
var a = monaco.editor.createModel('...', 'typescript');
var b = monaco.editor.createModel('...', 'typescript');
editor.setModel(a);
// hover or trigger some rich language feature
// ... (the TS worker has a synced to the web worker)
editor.setModel(b);
// hover or trigger some rich language feature
// ... (the TS worker has a and b synced to the web worker)
// ... (after some idle time)
// ... (the TS worker has b synced to the web worker)
// ... (after some idle time)
// ... (the TS worker has no models synced to the web worker)
// ... (after some idle time)
// ... (TS kills its web worker)
The underlying problem is that monaco-typescript is not built to allow for N individual compilation units (possibly configurable), but for a single compilation unit that contains all the typescript models.
To get these files to not interfere, I think you can dispose the models you don't want TypeScript to see. Alternatively, you can fork monaco-typescript to change the way it uses the TypeScript language service ... relevant line where it gives all the synced models to a single language service ... https://github.com/Microsoft/monaco-typescript/blob/master/src/worker.ts#L46
@alexandrudima, your suggested workaround of disposing my models worked! I now just add each model created with .createModel to a private array belonging to the component, and, on dispose, call
this._modelsToDispose.forEach(model => model.dispose());
It would be good to have this behavior documented somewhere (or if it is, but I didn't see it, maybe make it more prominent?... For visibility, I think that adding it to the Monaco Playground would also be good -- the playground was the single most useful place for me to learn about the editor options).
Thanks again for your help!
:+1: It would be awesome if you'd want to contribute a playground sample that shows / explains this.
https://github.com/Microsoft/monaco-editor/tree/master/website/playground/new-samples
all.js is the index and each sample has a css, html and js file.
Yes, please show us a fuller example!
Most helpful comment
@alexandrudima, your suggested workaround of disposing my models worked! I now just add each model created with
.createModelto a private array belonging to the component, and, on dispose, callIt would be good to have this behavior documented somewhere (or if it is, but I didn't see it, maybe make it more prominent?... For visibility, I think that adding it to the Monaco Playground would also be good -- the playground was the single most useful place for me to learn about the editor options).
Thanks again for your help!