LanguageServer
拡張機能エントリとは別に、サーバー用の.tsを作ります。
拡張機能が起動する際に、拡張機能からLanguageServerを起動してIPCで接続します。
これらはVSCodeのライブラリで簡単にできるようになっています。
LanguageServer:
https://github.com/Rockdoor/systemverilog/blob/master/src/server/server.ts
サンプルは公式にありますが、これがまた余計なものが多くてつかみにくい。要点はとても少ないです。
クライアント(拡張機能)との接続
サーバー側で以下のコードを記述。
let connection = lserv.createConnection(
new lserv.IPCMessageReader(process),
new lserv.IPCMessageWriter(process));
で完了します。基本これだけ。
リクエストに対するレスポンス
connection.onXXXXX() {
という形で、connectionのメンバとして 接頭on+LSPリクエストの関数を実装すると対応するリクエストにレスポンスできるようになります。
何に対応するかと言うのは onInitialize という必ず起動時にやってくるメッセージに対して下記のような hogehogeProviderの内容を返してやると拡張機能側でそのLSPが有効化されます。
return {
capabilities: {
textDocumentSync: documents.syncKind,
hoverProvider: true,
completionProvider: {
resolveProvider: true,
triggerCharacters: ['.', ':']
},
signatureHelpProvider: {
triggerCharacters: ['(', ',']
},
documentSymbolProvider: true,
definitionProvider: true,
referencesProvider: true,
// codeLensProvider: {
// resolveProvider: true
// },
// codeActionProvider: true,
executeCommandProvider: {
commands: ["sv.server.instantiateModule"]
},
}
};
実際の通信を垣間見る
公式でも紹介されていますが、
https://microsoft.github.io/language-server-protocol/inspector/
が便利です。
ログをダンプしておいて、それを上記ページにアップすると視覚化してくれます。
クライアント(拡張機能)からのLanguageServerの起動と接続
拡張機能が起動されると呼ばれる activate 関数内で以下の流れを記述するとサーバーが起動されます。
注意として、serverModuleのパスは、.ts の生成コード(.js)となります。
パスを含めて出力コードがサーチされるようにserverModuleを指定します。
// The server is implemented in node
let serverModule = ctx.asAbsolutePath(path.join('out', 'server', 'server.js'));
// The debug options for the server
let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] };
// If the extension is launched in debug mode then the debug server options are used
// Otherwise the run options are used
let serverOptions: vsclient.ServerOptions = {
run : { module: serverModule, transport: vsclient.TransportKind.ipc },
debug: { module: serverModule, transport: vsclient.TransportKind.ipc, options: debugOptions }
}
// Options to control the language client
let clientOptions: vsclient.LanguageClientOptions = {
documentSelector: [{scheme: 'file', language: 'sv'}],
synchronize: {
configurationSection: 'svlog',
fileEvents: vscode.workspace.createFileSystemWatcher('**/*.{v,sv,vh,svh}')
}
}
// Create the language client and start the client.
let disposable = new vsclient.LanguageClient('svlog', 'Language server for SystemVerilog', serverOptions, clientOptions).start();
あとは debugOption として、inspectを指定しておくと、そのプロセス番号を参照してVSCodeのデバッガがアタッチでき、サーバーの動きもトレースできるようになります。