Reputation: 3765
I want to be able to use the javascript-autocomplete of the monaco editor inside of markdown documents as well, because markdown documents can contain code snippets of type javascript:
```javascript
window.blah
```
I know that I could register a custom CompletionItemProvider for markdown, but I want to use the javascript one already provided by monaco. I have not found a way to receive their provider however.
Javascript syntax highlighting already works in markdown code blocks.
My idea was to somehow get their provider using something like this:
(await monaco.languages.typescript.getJavaScriptWorker()).something
For that to work I'd have to register javascript on the editor however. Even if I do that, I don't seem to be able to find anything on their worker that could help me.
While there's a method to register a CompletionItemProvider (https://microsoft.github.io/monaco-editor/api/modules/monaco.languages.html#registercompletionitemprovider), I couldn't find any that could help me get a registered provider.
How can I use the javascript-autocomplete in markdown code blocks as well?
Upvotes: 1
Views: 1285
Reputation: 53367
You are on the right track. Here's how I use typescript/javascript worker APIs to provide their completions in a mixed language environment:
return new Promise((resolve) => {
const workerPromise = (context.language === "javascript")
? languages.typescript.getJavaScriptWorker()
: languages.typescript.getTypeScriptWorker();
void workerPromise.then((worker: (...uris: Uri[]) => Promise<TypescriptWorker>) => {
void worker(model.uri).then((service) => {
const offset = model.getOffsetAt(localPosition);
const promise = service.getCompletionsAtPosition(model.uri.toString(), offset);
void promise.then((completions: CompletionInfo | undefined) => {
if (completions) {
const info = model.getWordUntilPosition(localPosition);
const replaceRange: IRange = {
startLineNumber: position.lineNumber,
startColumn: info.startColumn,
endLineNumber: position.lineNumber,
endColumn: info.endColumn,
};
resolve({
incomplete: false,
suggestions: completions.entries.map((entry) => ({
label: entry.name,
kind: this.convertKind(entry.kind),
range: replaceRange,
insertText: entry.insertText || entry.name,
} as CompletionItem)),
});
} else {
resolve({ incomplete: false, suggestions: [] });
}
});
});
});
});
Important here is that you create submodels with only the content of the parts in a single language (see Monaco.createModel()
) and use that to invoke the completion items code.
In my app I split the content of an editor into blocks (each covering only full lines and which have a specific language attached to them). So they have a start line and an end line. With this information I can create sub models for each block:
/**
* @returns A local model which contains only the text of this block. The caller must dispose of it!
*/
public get model(): Monaco.ITextModel {
const editorModel = super.model; // Model for the entire editor content.
if (!this.internalModel || this.internalModel.isDisposed()) {
const localModel = Monaco.createModel("", this.language);
// Do other required preparations of the local model, if needed.
this.internalModel = localModel;
}
if (editorModel && this.modelNeedsUpdate) {
this.modelNeedsUpdate = false;
this.internalModel.setValue(editorModel.getValueInRange(
{
startLineNumber: this.startLine,
startColumn: 1,
endLineNumber: this.endLine,
endColumn: editorModel.getLineMaxColumn(this.endLine),
}, Monaco.EndOfLinePreference.LF),
);
}
return this.internalModel;
}
Upvotes: 2