diff --git a/vscode-extensions/vscode-boot-java/lib/Main.ts b/vscode-extensions/vscode-boot-java/lib/Main.ts index 30b7d2bbc..986ef242e 100644 --- a/vscode-extensions/vscode-boot-java/lib/Main.ts +++ b/vscode-extensions/vscode-boot-java/lib/Main.ts @@ -2,36 +2,60 @@ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below +import * as net from 'net'; + import * as VSCode from 'vscode'; import * as Path from 'path'; import * as FS from 'fs'; import * as Net from 'net'; import * as ChildProcess from 'child_process'; -import {LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo} from 'vscode-languageclient'; -import {TextDocument} from 'vscode'; +import { LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, StreamInfo } from 'vscode-languageclient'; +import { TextDocument } from 'vscode'; +import { Trace } from 'vscode-jsonrpc'; import * as commons from 'commons-vscode'; -/** Called when extension is activated */ export function activate(context: VSCode.ExtensionContext) { - let options : commons.ActivatorOptions = { - DEBUG: true, - extensionId: 'vscode-boot-java', - fatJarFile: 'target/vscode-boot-java-0.0.1-SNAPSHOT.jar', - clientOptions: { - // HACK!!! documentSelector only takes string|string[] where string is language id, but DocumentFilter object is passed instead - // Reasons: - // 1. documentSelector is just passed over to functions like #registerHoverProvider(documentSelector, ...) that take documentSelector - // parameter in string | DocumentFilter | string[] | DocumentFilter[] format - // 2. Combination of non string|string[] documentSelector parameter and synchronize.textDocumentFilter function makes doc synchronization - // events pass on to Language Server only for documents for which function passed via textDocumentFilter property return true - // TODO: Remove cast ones https://github.com/Microsoft/vscode-languageserver-node/issues/9 is resolved + let CONNECT_TO_LS = false; + + if (CONNECT_TO_LS) { + let connectionInfo = { + port: 5007 + }; + let serverOptions = () => { + let socket = net.connect(connectionInfo); + let result: StreamInfo = { + writer: socket, + reader: socket + }; + return Promise.resolve(result); + }; + + let clientOptions: LanguageClientOptions = { documentSelector: ['java'], synchronize: { configurationSection: 'vscode-boot-java' } - } - }; - commons.activate(options, context); + }; + + let lc = new LanguageClient('vscode-boot-java', serverOptions, clientOptions); + lc.trace = Trace.Verbose; + let disposable = lc.start(); + context.subscriptions.push(disposable); + } + else { + let options: commons.ActivatorOptions = { + DEBUG: false, + extensionId: 'vscode-boot-java', + fatJarFile: 'target/vscode-boot-java-0.0.1-SNAPSHOT.jar', + clientOptions: { + documentSelector: ['java'], + synchronize: { + configurationSection: 'vscode-boot-java' + } + } + }; + commons.activate(options, context); + } } diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/StartupServer.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/StartupServer.java new file mode 100644 index 000000000..7ba13b8c7 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/StartupServer.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java; + +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.Launcher; +import org.eclipse.lsp4j.jsonrpc.MessageConsumer; +import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; + +/** + * starts up the language server and let it listen for connections from the outside + * instead of connecting itself to an existing port or channel. + * + * This is meant for development only, to reduce turnaround times while working + * on the language server from within an IDE, so that you can start the language + * server right away in debug mode and let the vscode extension connect to that + * instance instead of vice versa. + * + * Source of inspiration: + * https://github.com/itemis/xtext-languageserver-example/blob/master/org.xtext.example.mydsl.ide/src/org/xtext/example/mydsl/ide/RunServer.java + * + * @author Martin Lippert + */ +public class StartupServer { + + public static void main(String[] args) throws InterruptedException, IOException { + + JavaProjectFinder javaProjectFinder = BootJavaLanguageServer.DEFAULT_PROJECT_FINDER; + SimpleLanguageServer languageServer = new BootJavaLanguageServer(javaProjectFinder); + + Function wrapper = consumer -> { + MessageConsumer result = consumer; + return result; + }; + + Launcher launcher = createSocketLauncher(languageServer, STS4LanguageClient.class, new InetSocketAddress("localhost", 5007), Executors.newCachedThreadPool(), wrapper); + + languageServer.connect(launcher.getRemoteProxy()); + Future future = launcher.startListening(); + while (!future.isDone()) { + Thread.sleep(10_000l); + } + } + + private static Launcher createSocketLauncher(Object localService, Class remoteInterface, SocketAddress socketAddress, ExecutorService executorService, Function 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; + } + +}