how to insert text in monaco-editor including highlighted placeholders which are focused by hitting tab

Viewed 112

In monaco I can define autocomplete which inserts text with highlighted place holders, and when hitting the tab key it moves on to the next place holder,

For example, I define the following language on monaco:

monaco.languages.register({ id: 'mySpecialLanguage' });

// Register a completion item provider for the new language
monaco.languages.registerCompletionItemProvider('mySpecialLanguage', {
    provideCompletionItems: () => {
        var suggestions = [
            {
                label: 'if',
                kind: monaco.languages.CompletionItemKind.Keyword,
                insertText: 'if(${1:logical_expression}, ${2:value_if_true}, ${3:value_if_false})',
                insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
            }
        ];
        return { suggestions: suggestions };
    }
});

monaco.editor.create(document.getElementById('container'), {
    language: 'mySpecialLanguage'
});

I'm running above on the playground and this is the result when typing 'if' and hitting tab:

enter image description here

if I hit the tab the focused area moves from logical_expression to value_if_true, if I type on the keyboard text is inserted in the focused area.
attaching screen recording: enter image description here

Now I'm trying to insert text from an outside event (not via builtin CompletionItem) with the same behaviour as in the CompletionItem.
Text inserted: if(logical_expression, value_if_true, value_if_false) with placeholders highlighted and enable moving with tabs to focus on placeholders.
I tried the following

  monaco.editor.trigger('keyboard', 'type', {
    text: 'if(${1:logical_expression}, ${2:value_if_true}, ${3:value_if_false})',
    kind: monaco.languages.CompletionItemKind.Keyword,
    insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
  });

but this inserts the text as plain text without desired behaviour.
enter image description here How can I achieve above behaviour - similar to the autocomplete behaviour

1 Answers

There are a couple of ways to do this.

Option 1: Interact with snippet controller directly

You could get a reference to snippetController2 and call the insert() method passing in your template string as a parameter. e.g:

let contribution = editor.getContribution("snippetController2");
contribution.insert('if(${1:logical_expression}, ${2:value_if_true}, ${3:value_if_false})');

With this approach, the first placeholder logical_expression will not be highlighted, so you'll probably need to trigger another event afterwards to highlight this so the user can start typing straight away.

Option 2: Trigger autocomplete and select item programatically

This approach fires events to:

  • type the suggestion label (e.g. if)

  • trigger the autocomplete

  • select the suggestion that appears

// Type the word to trigger the suggestion e.g. "if"
editor.trigger('keyboard', 'type', {
    text: 'if',
    kind: monaco.languages.CompletionItemKind.Keyword,
    insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
  });
// trigger an "editor.action.triggerSuggest" action 
editor.trigger('if', 'editor.action.triggerSuggest', {});
// wait until suggestions have loaded and select the first one
setTimeout(() => {
    editor.trigger('editor', 'acceptSelectedSuggestion', {});
}, 50);

The disadvantage of this approach is that you see the autocomplete appear before the item is selected, but the cursor will be in the correct position for the user to start filling out the placeholders afterwards.

Here's a full code snippet you can use in the Monaco Playground. Just comment out whichever option you don't want in order to test the other one:

monaco.languages.register({ id: 'mySpecialLanguage' });

// Register a completion item provider for the new language
monaco.languages.registerCompletionItemProvider('mySpecialLanguage', {
    provideCompletionItems: () => {
        var suggestions = [
            {
                label: 'if',
                kind: monaco.languages.CompletionItemKind.Keyword,
                insertText: 'if(${1:logical_expression}, ${2:value_if_true}, ${3:value_if_false})',
                insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
            },
            {
                label: 'a',
                kind: monaco.languages.CompletionItemKind.Keyword,
                insertText: 'aaaa',
                insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
            }
        ];
        return { suggestions: suggestions };
    }
});

var editor = monaco.editor.create(document.getElementById('container'), {
    language: 'mySpecialLanguage'
});

/** Option 1 */
let contribution = editor.getContribution("snippetController2");
contribution.insert('if(${1:logical_expression}, ${2:value_if_true}, ${3:value_if_false})');
/** End Option 1 */

/** Option 2 */
// Type the word to trigger the suggestion e.g. "if"
editor.trigger('keyboard', 'type', {
    text: 'if',
    kind: monaco.languages.CompletionItemKind.Keyword,
    insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
  });
// trigger an "editor.action.triggerSuggest" action 
editor.trigger('if', 'editor.action.triggerSuggest', {});
// wait until suggestions have loaded and select the first one
setTimeout(() => {
    editor.trigger('editor', 'acceptSelectedSuggestion', {});
}, 50);
/** End Option 2 */
Related