Search code examples
javascripthtmlmonaco-editor

How do dynamically add objects to script field for auto complete?


is is possible to add objects for the script field to have access to when it runs?

for example, I want to add the following;

"current": { "name": "juan" }

So when the user is typing "curr", the auto complete will kick in and suggest 'current'

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.47.0/min/vs/loader.min.js"
    integrity="sha512-ZG31AN9z/CQD1YDDAK4RUAvogwbJHv6bHrumrnMLzdCrVu4HeAqrUX7Jsal/cbUwXGfaMUNmQU04tQ8XXl5Znw=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <title>Monaco Editor</title>
  <style>
    body, html {
      margin: 0;
      padding: 0;
      width: 100%;
      height: 100%;
      display: flex;
      overflow: hidden;
    }
    #editorArea, #output {
      flex: 1;
      min-width: 0;
    }
    #editorArea {
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }
    #toolbar {
      padding: 10px;
      background-color: #f5f5f5;
      border-bottom: 1px solid #ccc;
    }
    #editorContainer {
      flex-grow: 1;
      overflow: auto;
    }
    #container {
      width: 100%;
      height: 100%;
    }
    #outputArea {
      display: flex;
      flex-direction: column;
      flex: 1;
      overflow: hidden;
    }
    #outputToolbar {
      padding: 10px;
      background-color: #f5f5f5;
      border-bottom: 1px solid #ccc;
    }
    #output {
      flex-grow: 1;
      padding: 10px;
      overflow: auto;
      border-left: 1px solid #ddd;
    }
  </style>
</head>
<body>
  <div id="editorArea">
    <div id="toolbar">
      <select id="languageSelector">
        <option value="javascript">JavaScript</option>
      </select>
    </div>
    <div id="editorContainer">
      <div id="container"></div>
    </div>
  </div>
  <div id="outputArea">
    <div id="outputToolbar">
      <button id="runCodeButton">Run</button>
      <button id="exitEditorButton">Exit Editor</button>
    </div>
    <div id="output">Output will appear here...</div>
  </div>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
      require.config({
        paths: {
          'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.47.0/min/vs'
        }
      });
      require(['vs/editor/editor.main'], function () {
        const editor = monaco.editor.create(document.getElementById('container'), {
          value: "// Your code here\n",
          language: 'javascript',
          theme: 'vs-dark',
          automaticLayout: true
        });

        document.getElementById('languageSelector').addEventListener('change', function () {
          const newLanguage = this.value;
          monaco.editor.setModelLanguage(editor.getModel(), newLanguage);
        });

        // Define the global object
        const globalObject = { "current": { "name": "juan" } };

        // Provide IntelliSense support for the global object
        monaco.languages.typescript.javascriptDefaults.addExtraLib(`
          interface GlobalObject {
            current: { name: string };
          }
          declare var globalObject: GlobalObject;
        `);

        document.getElementById('runCodeButton').addEventListener('click', function () {
          const originalConsoleLog = console.log;
          document.getElementById('output').textContent = '';

          console.log = function (...args) {
            document.getElementById('output').textContent += args.join(' ') + '\n';
          };

          try {
            const userCode = editor.getModel().getValue();
            // Pass the global object to the eval context
            eval(`
              var globalObject = ${JSON.stringify(globalObject)};
              ${userCode}
            `);
          } catch (e) {
            document.getElementById('output').textContent = 'Error: ' + e.message;
          } finally {
            console.log = originalConsoleLog;
          }
        });
      });

      // Listen for messages from Flutter
      window.addEventListener('message', function(event) {
        if (event.data && event.data.type === 'initMonaco') {
          console.log('Data received from Flutter:', event.data.objects);
          // Handle the data received from Flutter as needed
        }
      });
    });
  </script>
</body>
</html>

The editor I am using is called Monaco Code Editor.

This stack overflow seems to be the same issue as mine:

Adding globally defined objects for Intellisense and linting to Monaco Editor in javascript/typescript


Solution

  • Yes, you can configure Monaco Editor to provide autocomplete suggestions for custom objects. Here's a modified version of your code to achieve this. The key steps are:

    Define a TypeScript type that includes your global object. Use monaco.languages.typescript.javascriptDefaults.addExtraLib to include this type in the editor. Make sure that the global object is available in the execution context. Here's how you can modify your existing code to include these steps:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.47.0/min/vs/loader.min.js"
        integrity="sha512-ZG31AN9z/CQD1YDDAK4RUAvogwbJHv6bHrumrnMLzdCrVu4HeAqrUX7Jsal/cbUwXGfaMUNmQU04tQ8XXl5Znw=="
        crossorigin="anonymous" referrerpolicy="no-referrer"></script>
      <title>Monaco Editor</title>
      <style>
        body, html {
          margin: 0;
          padding: 0;
          width: 100%;
          height: 100%;
          display: flex;
          overflow: hidden;
        }
        #editorArea, #output {
          flex: 1;
          min-width: 0;
        }
        #editorArea {
          display: flex;
          flex-direction: column;
          overflow: hidden;
        }
        #toolbar {
          padding: 10px;
          background-color: #f5f5f5;
          border-bottom: 1px solid #ccc;
        }
        #editorContainer {
          flex-grow: 1;
          overflow: auto;
        }
        #container {
          width: 100%;
          height: 100%;
        }
        #outputArea {
          display: flex;
          flex-direction: column;
          flex: 1;
          overflow: hidden;
        }
        #outputToolbar {
          padding: 10px;
          background-color: #f5f5f5;
          border-bottom: 1px solid #ccc;
        }
        #output {
          flex-grow: 1;
          padding: 10px;
          overflow: auto;
          border-left: 1px solid #ddd;
        }
      </style>
    </head>
    <body>
      <div id="editorArea">
        <div id="toolbar">
          <select id="languageSelector">
            <option value="javascript">JavaScript</option>
          </select>
        </div>
        <div id="editorContainer">
          <div id="container"></div>
        </div>
      </div>
      <div id="outputArea">
        <div id="outputToolbar">
          <button id="runCodeButton">Run</button>
          <button id="exitEditorButton">Exit Editor</button>
        </div>
        <div id="output">Output will appear here...</div>
      </div>
    
      <script>
        document.addEventListener('DOMContentLoaded', function () {
          require.config({
            paths: {
              'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.47.0/min/vs'
            }
          });
          require(['vs/editor/editor.main'], function () {
            const editor = monaco.editor.create(document.getElementById('container'), {
              value: "// Your code here\n",
              language: 'javascript',
              theme: 'vs-dark',
              automaticLayout: true
            });
    
            document.getElementById('languageSelector').addEventListener('change', function () {
              const newLanguage = this.value;
              monaco.editor.setModelLanguage(editor.getModel(), newLanguage);
            });
    
            // Define the global object and its type
            const globalObject = { "current": { "name": "juan" } };
    
            // Provide IntelliSense support for the global object
            monaco.languages.typescript.javascriptDefaults.addExtraLib(`
              interface GlobalObject {
                current: { name: string };
              }
              declare var globalObject: GlobalObject;
            `, 'globalObject.d.ts');
    
            document.getElementById('runCodeButton').addEventListener('click', function () {
              const originalConsoleLog = console.log;
              document.getElementById('output').textContent = '';
    
              console.log = function (...args) {
                document.getElementById('output').textContent += args.join(' ') + '\n';
              };
    
              try {
                const userCode = editor.getModel().getValue();
                // Pass the global object to the eval context
                eval(`
                  var globalObject = ${JSON.stringify(globalObject)};
                  ${userCode}
                `);
              } catch (e) {
                document.getElementById('output').textContent = 'Error: ' + e.message;
              } finally {
                console.log = originalConsoleLog;
              }
            });
          });
    
          // Listen for messages from Flutter
          window.addEventListener('message', function(event) {
            if (event.data && event.data.type === 'initMonaco') {
              console.log('Data received from Flutter:', event.data.objects);
              // Handle the data received from Flutter as needed
            }
          });
        });
      </script>
    </body>
    </html>