10

I am trying to provide intellisense / code completion into a javascript editor using the Monaco editor. The code needs to be valid javascript, not typescript.

Given some user entered script like this:

function onMyEvent(event)
{
    event.someProperty
}

I want to provide code completion on the event parameter, which is a typescript class I have the t.ds of, and can infer at runtime.

Ideally, I would just like to tell Monaco that the type of event is SomeEventClass, and let it do the rest. Even if that meant adding type hints to the script. But I can't see how to do that. I tried using JSDoc syntax and various combinations in the user script, but it looks like thats blocked FTB see: https://github.com/Microsoft/monaco-editor/issues/203 and Adding JavaScript type hints for VSCode/Monaco Intellisence

I also tried injecting a dynamic d.ts, as per https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-configure-javascript-defaults

But declaring the function didn't seem to mean anything to the editor. Declaring a new class definitely worked, I just can't work out how to tell Monaco that event in that function is a specific type.

I can see the registerCompletionItemProvider API, but that doesn't give you any context of where the item was declared etc, and also doesn't let me automatically use the d.ts file that I want to.

Jon N
  • 1,439
  • 1
  • 18
  • 29

3 Answers3

10

As of Monaco version 0.90, since https://github.com/Microsoft/monaco-editor/issues/203 has been fixed, you can add achieve this partially if you use JSDoc in the editing code.

For this code in the left side of the Monaco playgound:

    // validation settings
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: true,
    noSyntaxValidation: false
});

// compiler options
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
    target: monaco.languages.typescript.ScriptTarget.ES6,
    allowNonTsExtensions: true,
    allowJs: true
});

// extra libraries
monaco.languages.typescript.javascriptDefaults.addExtraLib([
    'declare class SomeEventType {',
    '    /**',
    '     * Heres the doco for someProperty',
    '     */',
    '    someProperty: string',
    '}',
].join('\n'), 'filename/facts.d.ts');

var jsCode = [
    '"use strict";',
    '',
    "/**",
    " * @param {SomeEventType} event",
    " */",
    "function onMyEvent(event) {",
    "",
    "}"
].join('\n');

monaco.editor.create(document.getElementById("container"), {
    value: jsCode,
    language: "javascript"
});

Means that the editor can now interpret the event parameter as a SomeEventType:

editor screenshot showing code complete

Jon N
  • 1,439
  • 1
  • 18
  • 29
  • 3
    Is there a possibility to achieve the same result without jsDoc comments? By e.g. using typescript? – knnhcn Oct 11 '18 at 18:30
3

This is how we do it for magikcraft.io: drop this code straight into the left pane in the playground, then hit Run:

monaco.editor.create(document.getElementById("container"), {
    value: "function hello() {\n\talert('Hello world!');\n}",
    language: "typescript"
});

const fact = `declare namespace custom { export function onMyEvent(event: customClass): void; 

export class customClass { 
    customProperty: string;
}`;
const factFilename = 'myCustomNamespace';
this.monaco.languages.typescript.typescriptDefaults.addExtraLib(fact, factFilename);

Now, in the right pane, then type: custom. and you'll get autocomplete for the custom facts.

Josh Wulf
  • 4,727
  • 2
  • 20
  • 34
  • Thanks - I tried that snippet in the monaco playgroud, couldn't get it to complete anything... would you expect from that that the user would be able to write a function `function onMyEvent(event) {} ` and get code complete on event? as opposed to calling onMyEvent? – Jon N Apr 06 '17 at 03:57
  • I edited my answer above to give a complete working example for the playground. – Josh Wulf Jul 19 '17 at 01:38
  • thanks for editing, but I don't think you're getting my use case. I want the user to type in a javascript function, and get code complete on the parameter. Eg the user types in function onMyEvent(event) {}, and inside the function the editor knows the type of event. – Jon N Jul 19 '17 at 05:34
3

Put this in the editor in the Monaco Playground:

monaco.editor.create(document.getElementById("container"), {
    value: "function hello() {\n\talert('Hello world!');\n}",
    language: "typescript"
});

const fact = `declare function onMyEvent(event: string): void; 
`;
const factFilename = 'myCustomNamespace1';
this.monaco.languages.typescript.typescriptDefaults.addExtraLib(fact, factFilename);

Now when you type onMyEvent in the right-hand pane, you'll get enter image description here

Josh Wulf
  • 4,727
  • 2
  • 20
  • 34
  • yes, that bit works. however the bit I really care about is the editor knowing the type of `event` within the function after you've declared it. In this code the editor thinks event is `any` once you've declared the function. See the image in my answer of the code complete that I'm after. – Jon N Jul 21 '17 at 10:18