monaco-editor 周りの調査
Monaco Editor 周りの資料が日本語にないので、見つけ次第追加していく
monaco で language server に繋ぐ場合とき、 monaco-languageclient で LSP コマンドを monaco 用に変換する
この実装が参考になる
vscode と monaco の highlighter は別実装。vscode は textmate 仕様の tslanguage format を使っている。
この正規表現は JS ではなく oniguruma 仕様で、wasm build を使ってコンパイルする必要がある。
(vscode web ビルドでは onigasm を使っている)
Code Highlighter が monaco に対応していなくて、 tmlanguage しかないとき、monaco-textmate を使って変換する必要がある。
magiql という graphql preview の実装を参考にして svelte の tmlanguage を動かそうとしたが、失敗した。後でもう一度やってみる。
素の manoco-editor は webpack plugin を要求してきて辛いが @monaco-editor/loader
を使えば jspm の prebuilt されたJSで読み込める。ただその分CDN経由で遅い。
monaco-languageclient の使い方
サーバーを建てずにブラウザで完結して monaco で補完したい。JS製の LSP Server をローカルで動かす monaco-languageclient を使いたい。そのために調べたこと。
基本的には https://github.com/TypeFox/monaco-languageclient/tree/master/examples/browser を見ればよい
概念整理
- LSP: Language Server Protocol - エディタで補完を行うためのプロトコル。基本的にはコードとカーソル位置を送って、補完候補をもらう。
- Language Service: サーバー側の実装。 ↑ のサンプルでは、 vscode-json-languageservice を使っている
- Language Client: クライアントが LSP を食う規約
- LSP Protocol: Language Service と Language Client が通信するためのプロトコル
概要
monaco のモデルを LSP 用のモデルに変換する。
一つのLSP変換関数があるわけではなく、language service を叩いて得られた結果をmonaco用に変換する
import {
MonacoToProtocolConverter,
ProtocolToMonacoConverter,
} from "monaco-languageclient/lib/monaco-converter";
const m2p = new MonacoToProtocolConverter();
const p2m = new ProtocolToMonacoConverter();
この変換プロトコルを使って jsonservice の doComplete を直接叩いて、それを monaco 用の provideCompletionItems に変換するコード。
monaco.languages.registerCompletionItemProvider(LANGUAGE_ID, {
provideCompletionItems(model, position, context, token): monaco.Thenable<monaco.languages.CompletionList> {
const document = createDocument(model);
const wordUntil = model.getWordUntilPosition(position);
const defaultRange = new monaco.Range(position.lineNumber, wordUntil.startColumn, position.lineNumber, wordUntil.endColumn);
const jsonDocument = jsonService.parseJSONDocument(document);
return jsonService.doComplete(document, m2p.asPosition(position.lineNumber, position.column), jsonDocument).then((list) => {
return p2m.asCompletionResult(list, defaultRange);
});
},
resolveCompletionItem(item, token): monaco.languages.CompletionItem | monaco.Thenable<monaco.languages.CompletionItem> {
return jsonService.doResolve(m2p.asCompletionItem(item)).then(result => p2m.asCompletionItem(result, item.range));
}
});
(非同期表現に独自の monaco.Thenable を使ってて、それが Promise と非互換なので async/await が使えない…)
LSPプロトコルを全部一括で変換するアダプタがあるわけではなく、機能ごとに個別に実装していくっぽい。