コード補完
コード補完は、補完タイミングに合わせてLanguageServerにLSPリクエストが来るので、それに答える形で実現します。
補完タイミングはVSCodeのデフォルトとして
- [Ctrl+Space] を押す
- 単語を数文字打つ
があり、さらに onInitialize で返信した
completionProvider: {
resolveProvider: true,
triggerCharacters: ['.', ':']
},
triggerCharactersが入力されたタイミングでも来ます。
これに対して connection.onCompletion で答えればOKです。
に、返信のデータ構造が示されています。
サーバー側での実装例です。
connection.onCompletion((_textDocumentPosition) => {
if (! init_done) return;
serverlog('onCompletion.');
let ctx = _textDocumentPosition.context;
let pos = _textDocumentPosition.position;
let uri = _textDocumentPosition.textDocument.uri;
let doc = documents.get(uri);
scanContent(doc.uri, doc.getText());
let items = sva.getCompleteItems(uri, pos.line + 1, pos.character);
return items;
});
scanContentがパーサーを起動しており、出来上がったデータベース(sva)からitemsを生成して返しています。
正しい構造でデータを返せれば、後はVSCodeのイケてるI/Fできれいに表示されるようになります。
データベースさえ作れれば、補完機能がこんなに簡単に実現できるので大変ありがたいです。
パーサーの実行タイミング
VSCodeで編集が行われる際、いつパーサーを使ってソースコードの解析を行い、データベースを更新するのかは自分で決めます。
候補としては、
- connection.onCompletion
- VSCodeがLanguageServerに補完を要求するタイミング
- documents.onDidChangeContent
- バッファ(ファイルではない)の内容に変化があった時
- documents.onDidSave
- ファイルの内容が変化した時(セーブした時)
などがあるでしょう。
documents.onDidChangeContent
はエディタのバッファが変化したときに反応しますので、1文字打つごとに実行されます。
このタイミングで解析すると一番リアルタイムな感じが出てカッコいい挙動を見せてくれます。
残念ながら、今回作ったSystemVerilogパーサーはかなり実行時間が長く、1000行前後から解析に数1000msを超え、レスポンスに難が出始めてしまいます。さらに悪いことに、onDidChangeContent イベントがキューされる挙動を見せるため、1つのリクエストに1000msもかかっていると、10文字打ち終わったら10秒かけて10回解析が走るという律儀な動作となり、最悪です。
onDidSaveだと適切なタイミングで補完が効かないため、結局、今回は補完タイミングでパーサーを走らせることにしました。