Xtextで作成したLSPサーバとVS Codeクライアント(LSPクライアント)をsocket通信で接続する
前提
– 「Eclipse Modeling ToolsでXtextプロジェクトをMavenプロジェクトとして作成する – 【Xtext】」参照
LSPサーバのランチャー実装(socket)
org.xtext.example.mydsl.ideプロジェクトのLSPサーバのランチャーをsocket通信で実装します。
package org.xtext.example.mydsl.ide;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.Channels;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Function;
import org.eclipse.lsp4j.jsonrpc.JsonRpcException;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.jsonrpc.MessageConsumer;
import org.eclipse.lsp4j.jsonrpc.MessageIssueException;
import org.eclipse.lsp4j.jsonrpc.messages.Message;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.xtext.ide.server.LanguageServerImpl;
import org.eclipse.xtext.ide.server.ServerModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class MyDslServerLauncher {
public static void main(String[] args) throws InterruptedException, IOException {
Injector injector = Guice.createInjector(new ServerModule());
LanguageServerImpl languageServer = injector.getInstance(LanguageServerImpl.class);
Function<MessageConsumer, MessageConsumer> wrapper = consumer -> {
MessageConsumer result = new MessageConsumer() {
@Override
public void consume(Message message) throws MessageIssueException, JsonRpcException {
System.out.println(message);
consumer.consume(message);
}
};
return result;
};
Launcher<LanguageClient> launcher = createSocketLauncher(languageServer, LanguageClient.class, new InetSocketAddress("localhost", 5007), Executors.newCachedThreadPool(), wrapper);
languageServer.connect(launcher.getRemoteProxy());
Future<?> future = launcher.startListening();
while (!future.isDone()) {
Thread.sleep(10_000l);
}
}
static <T> Launcher<T> createSocketLauncher(Object localService, Class<T> remoteInterface, SocketAddress socketAddress, ExecutorService executorService, Function<MessageConsumer, MessageConsumer> wrapper) throws IOException {
AsynchronousServerSocketChannel serverSocket = AsynchronousServerSocketChannel.open().bind(socketAddress);
AsynchronousSocketChannel socketChannel;
try {
socketChannel = serverSocket.accept().get();
return Launcher.createIoLauncher(localService, remoteInterface, Channels.newInputStream(socketChannel), Channels.newOutputStream(socketChannel), executorService, wrapper);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return null;
}
}
コード生成
org.xtext.example.mydsl.parentプロジェクトを選択して、右クリックから「Run As」-「Maven install」を選択します。
org.xtext.example.mydsl.ide/target配下に以下jarが作成されます。
- org.xtext.example.mydsl.ide-1.0.0-SNAPSHOT.jar(Eclipse(OSGi)環境で使用される jar)
- org.xtext.example.mydsl.ide-1.0.0-SNAPSHOT-ls.jar(fat jarはVS Codeで使用できる)
LSPサーバ起動
生成したjarでLSPサーバ起動します。
java -jar org.xtext.example.mydsl.ide-1.0.0-SNAPSHOT-ls.jar
extension.ts修正
extension.tsを修正します。
import * as vscode from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, StreamInfo } from 'vscode-languageclient/node';
import * as net from 'net';
let client: LanguageClient;
export async function activate(context: vscode.ExtensionContext) {
const connectionInfo = {
port: 5007
};
const serverOptions = () => {
// Connect to language server via socket
const socket = net.connect(connectionInfo);
const result: StreamInfo = {
writer: socket,
reader: socket
};
return Promise.resolve(result);
};
const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: 'file', language: 'mydsl' }],
synchronize: {
fileEvents: vscode.workspace.createFileSystemWatcher('**/*.mydsl')
}
};
client = new LanguageClient(
'mydslLanguageServer',
'MyDSL Language Server',
serverOptions,
clientOptions
);
await client.start();
context.subscriptions.push({
dispose: () => {
if (!client) {
return;
}
client.stop();
}
});
}
extension.tsを修正したら、npm run compileします。
デバッグ
F5でVS Code(拡張機能開発ホスト)を起動して確認します。

KHI入社して退社。今はCONFRAGEで正社員です。関西で140-170/80~120万から受け付けております^^
得意技はJS(ES20xx),Java,AWSの大体のリソースです
コメントはやさしくお願いいたします^^
座右の銘は、「狭き門より入れ」「願わくは、我に七難八苦を与えたまえ」です^^

