Monaco-editor: Duplicate IntelliSense definitions, complaints about duplicate identifiers, on reloading Monaco editor on single-page-app

Created on 8 Sep 2016  路  6Comments  路  Source: microsoft/monaco-editor

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!

Most helpful comment

@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!

All 6 comments

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:

  • when a rich language feature is requested for a model A
  • it begins syncing this model A to its web worker

    • it sends the initial content

    • and then events with contents deltas (such as when typing)

  • it stops syncing this model A:

    • when no rich language features requests have been received for a model for some time (think something like 5 min)

    • when the model A gets disposed

    • when the typescript configuration changes

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!

Was this page helpful?
0 / 5 - 0 ratings