mike-north / ember-monaco

Monaco editor for Ember.js apps
BSD 2-Clause "Simplified" License
18 stars 15 forks source link

How to access the Monaco instance to add custom languages? #328

Open xtagon opened 3 years ago

xtagon commented 3 years ago

Hi,

Is it possible to access the Monaco instance (not the editor instance) to add custom language/intellisense support?

I thought about modifying the onReady callback to provide both the editor and monaco instances from the iframe, but it seems to me this should really be done in an initializer before any editor components are initialized, not as a callback on individual editors. But I'm not sure how to accomplish that without access to the global.

Any ideas? Thanks!

dinager commented 2 years ago

I'm facing the same issue, @xtagon have you managed to figure out how to access the monaco instance for adding a custom language?

xtagon commented 2 years ago

@dinager I don't know if it can be done without modifying ember-monaco. One idea is to change these lines to return the Monaco instance and not just the editor instance, and then you can in theory use the onReady callback in any way you like: https://github.com/mike-north/ember-monaco/blob/master/addon/components/code-editor.ts#L42-L51

...but I haven't tried it yet

dinager commented 2 years ago

I managed to define a custom language using the current ember-monaco without modifying it. here is my editor running:

image

here is my code:
templates/application.hbs

<CodeEditor
  @language="mySpecialLanguage"
  @fillContent={{true}}
  @code={{this.sample1}}
  @onChange={{this.handleChange}}
  @theme="myCoolTheme"
  @onReady={{this.editorReady}}
>
</CodeEditor>

controllers.application.js

import {action} from '@ember/object';
import Ember from 'ember';

export default Ember.Controller.extend({
  sample1: "if($model.has_children, max($model.children[0].age, 10), \"none\")",

  @action
  editorReady(editor) {
    const monacoIframe = document.querySelector('iframe[src*="ember-monaco"]');
    const iframeWindow = monacoIframe.contentWindow;
    // Get the editor reference and set monaco global
    this.editor = iframeWindow.editor;
    this.monaco = iframeWindow.monaco;

    // Register a new language
    this.monaco.languages.register({id: 'mySpecialLanguage'});
    // Register a tokens provider for the language

    this.monaco.languages.setMonarchTokensProvider('mySpecialLanguage', {
      tokenizer: {
        root: [
          ['(if|max|min)', 'custom-function'],
          ['\\$[a-zA-Z.-_]*', 'custom-model'],
          ['[0-9]+', 'custom-number'],
          ['".*"', 'custom-string'],
        ],
      },
    });
    //--
    // Define a new theme that contains only rules that match this language
    this.monaco.editor.defineTheme('myCoolTheme', {
      base: 'vs',
      inherit: false,
      rules: [
        {token: 'custom-function', foreground: '#0000FF', fontStyle: 'bold'},
        {token: 'custom-model', foreground: 'FFA500', fontStyle: 'bold'},
        {token: 'custom-number', foreground: '008800'},
        {token: 'custom-string', foreground: 'ff0000'}
      ],
      colors: {
        'editor.foreground': '#000000',
      }
    });
    this.monaco.editor.setTheme('myCoolTheme');

    this.monaco.languages.registerCompletionItemProvider('mySpecialLanguage', {
      provideCompletionItems: () => {
        var suggestions = [
          {
            label: 'ifelse',
            kind: this.monaco.languages.CompletionItemKind.Snippet,
            insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'),
            insertTextRules: this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
            documentation: 'If-Else Statement',
          },
        ];
        return {suggestions: suggestions};
      }
    });

  }
})