suren-atoyan / monaco-react

Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins
https://monaco-react.surenatoyan.com/
MIT License
3.65k stars 262 forks source link

Set up listeners when a model is created or disposed for @monaco-editor/react #510

Closed chengtie closed 1 year ago

chengtie commented 1 year ago

I have a web application containing a Monaco Editor. Previously, I used react-monaco-editor, now for some reasons, I'm thinking of switching to @monaco-editor/react.

With react-monaco-editor, I was able to code a DiagnosticsAdapter class to provide real-time error checking and suggestions in a code editor. When a user types code into the editor, the adapter will validate the code and provide feedback on any issues it finds.

Now, with @monaco-editor/react, the DiagnosticsAdapter does not work anymore: code in onModelAdd and code in onModelRemoved is never executed.

enter image description here

Does anyone know how to set up these listeners for @monaco-editor/react and make DiagnosticsAdapter work?

Here is CodeSandBox: https://codesandbox.io/s/aged-voice-5rz8z2?file=/src/App.js:0-542

Here is language-feature.ts:

import * as monaco from "monaco-editor";

export class DiagnosticsAdapter {
  private _disposables: monaco.IDisposable[] = [];
  private _listener = Object.create(null);

  constructor() {
    console.log("herehere in constructor");

    const onModelAdd = (model: monaco.editor.IModel): void => {
      console.log("herehere onModelAdd");

      let handle;
      const changeSubscription = model.onDidChangeContent(() => {
        clearTimeout(handle);
        handle = setTimeout(() => console.log("call validation"), 500);
      });

      this._listener[model.uri.toString()] = {
        dispose() {
          changeSubscription.dispose();
          clearTimeout(handle);
        }
      };

      console.log("call vailidation");
    };

    const onModelRemoved = (model: monaco.editor.IModel): void => {
      console.log("herehere onModelRemoved");
      monaco.editor.setModelMarkers(model, "abc", []);
      const key = model.uri.toString();
      if (this._listener[key]) {
        this._listener[key].dispose();
        delete this._listener[key];
      }
    };

    console.log("herehere 5");
    this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd));
    console.log("herehere 6");
    this._disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved));
    console.log("herehere 7");
    this._disposables.push(
      monaco.editor.onDidChangeModelLanguage((event) => {
        console.log("herehere 8");
        onModelRemoved(event.model);
        console.log("herehere 9");
        onModelAdd(event.model);
      })
    );

    console.log("herehere 10");
    this._disposables.push({
      dispose() {
        for (const model of monaco.editor.getModels()) {
          onModelRemoved(model);
        }
      }
    });

    console.log("herehere 11");
    monaco.editor.getModels().forEach(onModelAdd);
  }

  public dispose(): void {
    this._disposables.forEach((d) => d && d.dispose());
    this._disposables = [];
  }
}

Here is App.js:

import React from "react";
import Editor from "@monaco-editor/react";
import { DiagnosticsAdapter } from "./language-feature";

function App() {
  function handleEditorDidMount(editor, monaco) {
    monaco.languages.register({ id: "mySpecialLanguage" });

    let x = new DiagnosticsAdapter();
  }

  return (
    <div className="App">
      <Editor
        height="90vh"
        defaultLanguage="mySpecialLanguage"
        defaultValue="// some comment"
        onMount={handleEditorDidMount}
      />
    </div>
  );
}

export default App;
suren-atoyan commented 1 year ago

Hi @chengtie. The first issue I've noticed is that you use different monaco instances in App.js and language-feature.ts. @monaco-editor/react doesn't use monaco instance from monaco-editor package by default. Instead, it downloads the instance from CDN. To change that behavior check this.

chengtie commented 1 year ago

Hi @chengtie. The first issue I've noticed is that you use different monaco instances in App.js and language-feature.ts. @monaco-editor/react doesn't use monaco instance from monaco-editor package by default. Instead, it downloads the instance from CDN. To change that behavior check this.

Thank you for the help.

I now pass monaco instance from App.js to DiagnosticsAdapter when constructing a DiagnosticsAdapter. It works.

Here is a working CodeSandbox: https://codesandbox.io/s/vibrant-sun-gzl8nh.

You could close the issue. Cheers.