Convert language servers to Boot apps
This commit is contained in:
@@ -16,15 +16,13 @@
|
||||
<dependencies.version>${project.version}</dependencies.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>distribution-repository</id>
|
||||
<name>Temporary Staging Repository</name>
|
||||
<url>file://${basedir}/dist</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- spring boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-starter</artifactId>
|
||||
<version>${dependencies.version}</version>
|
||||
</dependency>
|
||||
<!-- Language Servers -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.time.Duration;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.Settings;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Provides access to configuration options that allow user to
|
||||
@@ -22,6 +23,7 @@ import org.springframework.ide.vscode.commons.util.Log;
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
@Component
|
||||
public class BoshCliConfig {
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ package org.springframework.ide.vscode.bosh;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -29,6 +30,9 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class BoshDefintionFinder extends SimpleDefinitionFinder<BoshLanguageServer> {
|
||||
@@ -44,7 +48,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder<BoshLanguageServ
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Handler {
|
||||
Flux<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
|
||||
List<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
|
||||
}
|
||||
|
||||
public BoshDefintionFinder(BoshLanguageServer server, BoshSchemas schema, YamlAstCache asts, ASTTypeCache astTypes) {
|
||||
@@ -64,7 +68,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder<BoshLanguageServ
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Flux<Location> findDefinitions(TextDocumentPositionParams params) {
|
||||
public List<Location> handle(TextDocumentPositionParams params) {
|
||||
try {
|
||||
TextDocument doc = server.getTextDocumentService().get(params);
|
||||
if (doc!=null) {
|
||||
@@ -85,7 +89,7 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder<BoshLanguageServ
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return Flux.empty();
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,14 +105,19 @@ public class BoshDefintionFinder extends SimpleDefinitionFinder<BoshLanguageServ
|
||||
String name = NodeUtil.asScalar(refNode);
|
||||
if (name!=null) {
|
||||
Collection<Node> candidates = astTypes.getNodes(uri, def);
|
||||
return Flux.fromIterable(candidates)
|
||||
.filter((node) -> name.equals(NodeUtil.asScalar(node)))
|
||||
.map((node) -> toLocation(doc, node))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get);
|
||||
Builder<Location> definitions = ImmutableList.builder();
|
||||
for (Node node : candidates) {
|
||||
if (name.equals(NodeUtil.asScalar(node))) {
|
||||
Optional<Location> loc = toLocation(doc, node);
|
||||
if (loc.isPresent()) {
|
||||
definitions.add(loc.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return definitions.build();
|
||||
}
|
||||
}
|
||||
return Flux.empty();
|
||||
return ImmutableList.of();
|
||||
};
|
||||
handlers.put(ref, handler);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-2017 Pivotal, Inc.
|
||||
* Copyright (c) 2018 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
|
||||
@@ -10,24 +10,35 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.bosh;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ide.vscode.bosh.models.BoshCommandCloudConfigProvider;
|
||||
import org.springframework.ide.vscode.bosh.models.BoshCommandReleasesProvider;
|
||||
import org.springframework.ide.vscode.bosh.models.BoshCommandStemcellsProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
String serverName = "bosh-language-server";
|
||||
LogRedirect.redirectToFile(serverName);
|
||||
BoshCliConfig cliConfig = new BoshCliConfig();
|
||||
LaunguageServerApp.start(serverName, () -> new BoshLanguageServer(
|
||||
@SpringBootApplication
|
||||
public class BoshLanguageServerBootApp {
|
||||
|
||||
private static final String SERVER_NAME = "bosh-language-server";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead.
|
||||
SpringApplication.run(BoshLanguageServerBootApp.class, args);
|
||||
}
|
||||
|
||||
@Bean public String serverName() {
|
||||
return SERVER_NAME;
|
||||
}
|
||||
|
||||
@Bean BoshLanguageServer languageServer(BoshCliConfig cliConfig) {
|
||||
return new BoshLanguageServer(
|
||||
cliConfig,
|
||||
new BoshCommandCloudConfigProvider(cliConfig),
|
||||
new BoshCommandStemcellsProvider(cliConfig),
|
||||
new BoshCommandReleasesProvider(cliConfig)
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import java.util.Collection;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -34,8 +34,11 @@ import org.eclipse.lsp4j.jsonrpc.MessageConsumer;
|
||||
import org.eclipse.lsp4j.services.LanguageClient;
|
||||
import org.eclipse.lsp4j.services.LanguageClientAware;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LoggingFormat;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.AsyncRunner;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
|
||||
|
||||
@@ -53,37 +56,48 @@ import org.springframework.ide.vscode.commons.util.Log;
|
||||
* @author Kris De Volder
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public abstract class LaunguageServerApp {
|
||||
public class LaunguageServerApp {
|
||||
|
||||
public static final String STS4_LANGUAGESERVER_NAME = "sts4.languageserver.name";
|
||||
public static final String STANDALONE_STARTUP = "standalone-startup";
|
||||
private static final int SERVER_STANDALONE_PORT = 5007;
|
||||
|
||||
public static void start(String name, Provider<SimpleLanguageServer> languageServerFactory) throws IOException, InterruptedException {
|
||||
System.setProperty(STS4_LANGUAGESERVER_NAME, name); //makes it easy to recognize language server processes.
|
||||
LaunguageServerApp app = new LaunguageServerApp() {
|
||||
@Override
|
||||
protected SimpleLanguageServer createServer() {
|
||||
return languageServerFactory.get();
|
||||
}
|
||||
};
|
||||
final static Logger log = LoggerFactory.getLogger(LaunguageServerApp.class);
|
||||
|
||||
private final String name;
|
||||
private final Provider<SimpleLanguageServer> languageServerFactory;
|
||||
|
||||
public LaunguageServerApp(String name, Provider<SimpleLanguageServer> languageServerFactory) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.languageServerFactory = languageServerFactory;
|
||||
}
|
||||
|
||||
public void start() throws Exception {
|
||||
System.setProperty(STS4_LANGUAGESERVER_NAME, name); //makes it easy to recognize language server processes.
|
||||
LaunguageServerApp app = this;
|
||||
if (System.getProperty(STANDALONE_STARTUP, "false").equals("true")) {
|
||||
app.startAsServer();
|
||||
}
|
||||
else {
|
||||
app.start();
|
||||
} else {
|
||||
app.startAsClient();
|
||||
}
|
||||
}
|
||||
|
||||
public static void startAsServer(Provider<SimpleLanguageServer> languageServerFactory) throws IOException, InterruptedException {
|
||||
LaunguageServerApp app = new LaunguageServerApp() {
|
||||
@Override
|
||||
protected SimpleLanguageServer createServer() {
|
||||
return languageServerFactory.get();
|
||||
}
|
||||
};
|
||||
app.startAsServer();
|
||||
public void startAsync() {
|
||||
//TODO: feel a bit wasteful to have thread dedicated to just waiting for the server to stop.
|
||||
// Not sure how we can really avoid this though. Lsp4j is providing
|
||||
// lots of api that returns Futures which the only way to deal with them is blocking threads calling
|
||||
// their get method.
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
start();
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
}
|
||||
},
|
||||
"LanguageServerApp lifecycle"
|
||||
).start();
|
||||
}
|
||||
|
||||
protected static class Connection {
|
||||
@@ -122,14 +136,14 @@ public abstract class LaunguageServerApp {
|
||||
}
|
||||
}
|
||||
|
||||
public void start() throws IOException {
|
||||
public void startAsClient() throws IOException {
|
||||
Log.info("Starting LS");
|
||||
Connection connection = null;
|
||||
try {
|
||||
LoggingFormat.startLogging();
|
||||
connection = connectToNode();
|
||||
|
||||
run(connection);
|
||||
runAsync(connection).get();
|
||||
} catch (Throwable t) {
|
||||
Log.log(t);
|
||||
System.exit(1);
|
||||
@@ -152,8 +166,8 @@ public abstract class LaunguageServerApp {
|
||||
* 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
|
||||
*/
|
||||
public void startAsServer() throws IOException, InterruptedException {
|
||||
Log.info("Starting LS as standlone server port = "+SERVER_STANDALONE_PORT);
|
||||
public void startAsServer() throws Exception {
|
||||
log.info("Starting LS as standlone server port = {}", SERVER_STANDALONE_PORT);
|
||||
|
||||
Function<MessageConsumer, MessageConsumer> wrapper = consumer -> {
|
||||
MessageConsumer result = consumer;
|
||||
@@ -165,33 +179,30 @@ public abstract class LaunguageServerApp {
|
||||
new InetSocketAddress("localhost", SERVER_STANDALONE_PORT), createServerThreads(), wrapper);
|
||||
|
||||
languageServer.connect(launcher.getRemoteProxy());
|
||||
Future<?> future = launcher.startListening();
|
||||
while (!future.isDone()) {
|
||||
Thread.sleep(10_000l);
|
||||
}
|
||||
launcher.startListening().get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the thread pool / executor passed to lsp4j server intialization. From the looks of things,
|
||||
* @return
|
||||
*/
|
||||
protected ExecutorService createServerThreads() {
|
||||
protected ExecutorService createServerThreads() {
|
||||
return Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
private <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;
|
||||
}
|
||||
private <T> Launcher<T> createSocketLauncher(
|
||||
Object localService, Class<T> remoteInterface,
|
||||
SocketAddress socketAddress, ExecutorService executorService,
|
||||
Function<MessageConsumer, MessageConsumer> wrapper
|
||||
) throws Exception {
|
||||
AsynchronousServerSocketChannel serverSocket = AsynchronousServerSocketChannel.open().bind(socketAddress);
|
||||
AsynchronousSocketChannel socketChannel = serverSocket.accept().get();
|
||||
log.info("Client connected via socket");
|
||||
return Launcher.createIoLauncher(localService, remoteInterface, Channels.newInputStream(socketChannel),
|
||||
Channels.newOutputStream(socketChannel), executorService, wrapper);
|
||||
}
|
||||
|
||||
private static Connection connectToNode() throws IOException {
|
||||
private static Connection connectToNode() throws IOException {
|
||||
String port = System.getProperty("server.port");
|
||||
|
||||
if (port != null) {
|
||||
@@ -217,10 +228,8 @@ public abstract class LaunguageServerApp {
|
||||
* Listen for requests from the parent node process.
|
||||
* Send replies asynchronously.
|
||||
* When the request stream is closed, wait for 5s for all outstanding responses to compute, then return.
|
||||
* @throws ExecutionException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
protected void run(Connection connection) throws InterruptedException, ExecutionException {
|
||||
protected Future<Void> runAsync(Connection connection) throws Exception {
|
||||
LanguageServer server = createServer();
|
||||
ExecutorService executor = createServerThreads();
|
||||
Function<MessageConsumer, MessageConsumer> wrapper = (MessageConsumer consumer) -> {
|
||||
@@ -229,7 +238,7 @@ public abstract class LaunguageServerApp {
|
||||
consumer.consume(msg);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
//log a warning and ignore. We are getting some messages from vsCode the server doesn't know about
|
||||
Log.warn("Unsupported message was ignored!", e);
|
||||
log.warn("Unsupported message was ignored!", e);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -246,9 +255,10 @@ public abstract class LaunguageServerApp {
|
||||
((LanguageClientAware) server).connect(client);
|
||||
}
|
||||
|
||||
launcher.startListening().get();
|
||||
return launcher.startListening();
|
||||
}
|
||||
|
||||
protected abstract SimpleLanguageServer createServer();
|
||||
|
||||
final SimpleLanguageServer createServer() {
|
||||
return languageServerFactory.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.commons.languageserver.definition;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.TextDocumentPositionParams;
|
||||
@@ -20,7 +20,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* {@link SimpleDefinitionFinder} provides a 'dummy' implementation of
|
||||
@@ -35,21 +35,7 @@ public class SimpleDefinitionFinder<T extends SimpleLanguageServer> implements D
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> handle(TextDocumentPositionParams position) {
|
||||
return findDefinitions(position)
|
||||
.collect(Collectors.toList()).block();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is meant to be overridden by subclass. This method provides a simple implementation
|
||||
* of 'goto definition' (which is not one you probably want to use in practice, but it
|
||||
* might be usful just to test whether things are wired up correctly to make the
|
||||
* 'goto definition' action in vscode work.
|
||||
* <p>
|
||||
* The implementation provided here simply looks for the first occurrence of the word
|
||||
* currently pointed at in the current document using String.indexOf.
|
||||
*/
|
||||
protected Flux<Location> findDefinitions(TextDocumentPositionParams params) {
|
||||
public List<Location> handle(TextDocumentPositionParams params) {
|
||||
try {
|
||||
TextDocument doc = server.getTextDocumentService().get(params);
|
||||
if (doc != null) {
|
||||
@@ -68,20 +54,17 @@ public class SimpleDefinitionFinder<T extends SimpleLanguageServer> implements D
|
||||
String text = doc.get();
|
||||
int def = text.indexOf(word);
|
||||
if (def>=0) {
|
||||
return Flux.just(
|
||||
new Location(params.getTextDocument().getUri(),
|
||||
doc.toRange(def, word.length())
|
||||
)
|
||||
)
|
||||
.doOnNext((Location loc) -> {
|
||||
Log.log("definition: "+loc);
|
||||
});
|
||||
Location loc = new Location(params.getTextDocument().getUri(),
|
||||
doc.toRange(def, word.length())
|
||||
);
|
||||
Log.log("definition: "+loc);
|
||||
return ImmutableList.of(loc);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return Flux.empty();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -144,7 +144,11 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
|
||||
|
||||
private Map<String, ExecuteCommandHandler> commands = new HashMap<>();
|
||||
|
||||
private AsyncRunner async = new AsyncRunner(Schedulers.newSingle("SimpleLanguaserver main thread"));
|
||||
private AsyncRunner async = new AsyncRunner(Schedulers.newSingle(runable -> {
|
||||
Thread t = new Thread(runable, "SimpleLanguaserver main thread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}));
|
||||
private ClasspathListenerManager classpathListenerManager;
|
||||
|
||||
@Override
|
||||
@@ -214,7 +218,7 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
|
||||
|
||||
@Override
|
||||
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
|
||||
Log.debug("Initializing: "+params);
|
||||
log.info("Initializing: "+params);
|
||||
|
||||
// multi-root workspace handling
|
||||
List<WorkspaceFolder> workspaceFolders = getWorkspaceFolders(params);
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.slf4j.Logger;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
public class AsyncRunner {
|
||||
|
||||
|
||||
@@ -17,12 +17,24 @@ import java.io.PrintStream;
|
||||
|
||||
public class LogRedirect {
|
||||
|
||||
public static void bootRedirectToFile(String name) throws IOException {
|
||||
String logfilePath = System.getProperty("sts.log.file");
|
||||
if (StringUtil.hasText(logfilePath)) {
|
||||
File logfile = new File(logfilePath);
|
||||
System.err.println("Redirecting log output to: "+logfile);
|
||||
PrintStream logFile = new PrintStream(new FileOutputStream(logfile, false));
|
||||
System.setErr(logFile);
|
||||
System.setOut(logFile); //Spring boot actually logs on sysout instead of syserr.
|
||||
}
|
||||
}
|
||||
|
||||
public static void redirectToFile(String name) throws IOException {
|
||||
String logfilePath = System.getProperty("sts.log.file");
|
||||
if (StringUtil.hasText(logfilePath)) {
|
||||
File logfile = new File(logfilePath);
|
||||
System.err.println("Redirecting log output to: "+logfile);
|
||||
System.setErr(new PrintStream(new FileOutputStream(logfile, false)));
|
||||
PrintStream logFile = new PrintStream(new FileOutputStream(logfile, false));
|
||||
System.setErr(logFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
28
headless-services/commons/language-server-starter/pom.xml
Normal file
28
headless-services/commons/language-server-starter/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>language-server-starter</artifactId>
|
||||
<name>language-server-starter</name>
|
||||
<description>Spring Boot Starter for building Language Server</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-parent</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-language-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springframework.ide.vscode.languageserver.starter;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
|
||||
@Configuration
|
||||
public class LanguageServerAutoconf {
|
||||
|
||||
@Bean public LaunguageServerApp serverApp(
|
||||
@Qualifier("serverName") String serverName,
|
||||
Provider<SimpleLanguageServer> languageServerFactory
|
||||
) {
|
||||
return new LaunguageServerApp(serverName, languageServerFactory);
|
||||
}
|
||||
|
||||
@Bean public CommandLineRunner serverStarter(LaunguageServerApp serverApp) {
|
||||
return args -> {
|
||||
serverApp.startAsync();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,12 @@
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<name>commons-parent</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.0.5.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
<module>commons-language-server</module>
|
||||
<module>language-server-test-harness</module>
|
||||
@@ -20,6 +26,8 @@
|
||||
<module>commons-maven</module>
|
||||
<module>commons-gradle</module>
|
||||
<module>commons-boot-app-cli</module>
|
||||
|
||||
<module>language-server-starter</module>
|
||||
</modules>
|
||||
|
||||
<repositories>
|
||||
@@ -74,14 +82,13 @@
|
||||
<yaml-version>1.17</yaml-version>
|
||||
<junit-version>4.11</junit-version>
|
||||
<assertj-version>3.5.2</assertj-version>
|
||||
<slf4j-version>1.7.22</slf4j-version>
|
||||
<slf4j-version>1.7.25</slf4j-version>
|
||||
<guava-version>19.0</guava-version>
|
||||
<mockito-version>1.10.19</mockito-version>
|
||||
<jackson-2-version>2.5.0</jackson-2-version>
|
||||
<jersey-2-version>2.10</jersey-2-version>
|
||||
<lsp4j-version>0.4.0-SNAPSHOT</lsp4j-version>
|
||||
<cglib-version>3.2.7</cglib-version>
|
||||
<boot-version>2.0.4.RELEASE</boot-version>
|
||||
<!-- NOTE: Reactor version must match version used by the CF client -->
|
||||
<cloudfoundry-client-version>3.8.0.RELEASE</cloudfoundry-client-version>
|
||||
<reactor-version>3.1.5.RELEASE</reactor-version>
|
||||
@@ -111,11 +118,11 @@
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>${slf4j-version}</version>
|
||||
</dependency>
|
||||
</dependency> -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
||||
@@ -25,10 +25,16 @@
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- spring boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Language Servers -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-language-server</artifactId>
|
||||
<artifactId>language-server-starter</artifactId>
|
||||
<version>${dependencies.version}</version>
|
||||
</dependency>
|
||||
<!-- Yaml -->
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.concourse;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -28,13 +30,16 @@ import org.springframework.ide.vscode.commons.yaml.reconcile.ASTTypeCache;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseLanguageServer> {
|
||||
|
||||
@FunctionalInterface
|
||||
private interface Handler {
|
||||
Flux<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
|
||||
List<Location> handle(Node refNode, TextDocument doc, YamlFileAST ast);
|
||||
}
|
||||
|
||||
private final ASTTypeCache astTypes;
|
||||
@@ -64,19 +69,24 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseL
|
||||
Handler handler = (Node refNode, TextDocument doc, YamlFileAST ast) -> {
|
||||
String name = NodeUtil.asScalar(refNode);
|
||||
if (name!=null) {
|
||||
return Flux.fromStream(definitionsPath.traverseAmbiguously(ast))
|
||||
.filter((node) -> name.equals(NodeUtil.asScalar(node)))
|
||||
.map((node) -> toLocation(doc, node))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get);
|
||||
Builder<Location> definitions = ImmutableList.builder();
|
||||
definitionsPath.traverseAmbiguously(ast).forEach(node -> {
|
||||
if (name.equals(NodeUtil.asScalar(node))) {
|
||||
Optional<Location> loc = toLocation(doc, node);
|
||||
if (loc.isPresent()) {
|
||||
definitions.add(loc.get());
|
||||
}
|
||||
}
|
||||
});
|
||||
return definitions.build();
|
||||
}
|
||||
return Flux.empty();
|
||||
return ImmutableList.of();
|
||||
};
|
||||
handlers.put(refType, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Flux<Location> findDefinitions(TextDocumentPositionParams params) {
|
||||
public List<Location> handle(TextDocumentPositionParams params) {
|
||||
try {
|
||||
TextDocument doc = server.getTextDocumentService().get(params);
|
||||
if (doc!=null) {
|
||||
@@ -97,7 +107,7 @@ public class ConcourseDefinitionFinder extends SimpleDefinitionFinder<ConcourseL
|
||||
} catch (Exception e) {
|
||||
Log.log(e);
|
||||
}
|
||||
return Flux.empty();
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
Optional<Location> toLocation(TextDocument doc, Node node) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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.concourse;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
import org.springframework.ide.vscode.concourse.github.DefaultGithubInfoProvider;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ConcourseLanguageServerBootApp {
|
||||
|
||||
private static final String SERVER_NAME = "concourse-language-server";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead.
|
||||
SpringApplication.run(ConcourseLanguageServerBootApp.class, args);
|
||||
}
|
||||
|
||||
@Bean public String serverName() {
|
||||
return SERVER_NAME;
|
||||
}
|
||||
|
||||
@Bean ConcourseLanguageServer languageServer() {
|
||||
return new ConcourseLanguageServer(YamlCompletionEngineOptions.DEFAULT, new DefaultGithubInfoProvider());
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016-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.concourse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
import org.springframework.ide.vscode.concourse.github.DefaultGithubInfoProvider;
|
||||
import org.springframework.ide.vscode.concourse.github.GithubInfoProvider;
|
||||
|
||||
import static org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp.STANDALONE_STARTUP;
|
||||
|
||||
public class Main {
|
||||
private static final YamlCompletionEngineOptions OPTIONS = YamlCompletionEngineOptions.DEFAULT;
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
String serverName = "concourse-language-server";
|
||||
if (!Boolean.getBoolean(STANDALONE_STARTUP)) {
|
||||
LogRedirect.redirectToFile(serverName);
|
||||
}
|
||||
LaunguageServerApp.start(serverName, () -> new ConcourseLanguageServer(OPTIONS, new DefaultGithubInfoProvider()));
|
||||
}
|
||||
}
|
||||
@@ -128,11 +128,11 @@ public class DefaultGithubInfoProvider implements GithubInfoProvider {
|
||||
return reposByOwner.get(ownerName, loader(() -> {
|
||||
GHPerson owner = getOwner(ownerName);
|
||||
if (owner!=null) {
|
||||
return Flux.fromIterable(owner.listRepositories())
|
||||
.filter(repo -> repo.getOwnerName().equals(ownerName))
|
||||
.map(GHRepository::getName)
|
||||
.collect(CollectorUtil.toImmutableSet())
|
||||
.block();
|
||||
ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||
for (GHRepository repo : owner.listRepositories()) {
|
||||
builder.add(repo.getName());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return null;
|
||||
}))
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- spring boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-starter</artifactId>
|
||||
<version>${dependencies.version}</version>
|
||||
</dependency>
|
||||
<!-- Language Servers -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
@@ -47,6 +53,17 @@
|
||||
<version>${dependencies.version}</version>
|
||||
</dependency>
|
||||
<!-- CF -->
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-core</artifactId>
|
||||
<version>${reactor-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.ipc</groupId>
|
||||
<artifactId>reactor-netty</artifactId>
|
||||
<version>${reactor-netty}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>commons-cf</artifactId>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 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.manifest.yaml;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
|
||||
public class Main {
|
||||
SimpleLanguageServer server = new ManifestYamlLanguageServer();
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
String serverName = "manifest-yaml-language-server";
|
||||
LogRedirect.redirectToFile(serverName);
|
||||
LaunguageServerApp.start(serverName, ManifestYamlLanguageServer::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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.manifest.yaml;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
import org.springframework.ide.vscode.commons.yaml.completion.YamlCompletionEngineOptions;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ManifestYamlLanguageServerBootApp {
|
||||
|
||||
private static final String SERVER_NAME = "manifest-yaml-language-server";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead.
|
||||
SpringApplication.run(ManifestYamlLanguageServerBootApp.class, args);
|
||||
}
|
||||
|
||||
@Bean public String serverName() {
|
||||
return SERVER_NAME;
|
||||
}
|
||||
|
||||
@Bean ManifestYamlLanguageServer languageServer() {
|
||||
return new ManifestYamlLanguageServer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,8 +24,14 @@
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<!-- spring boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ide.vscode</groupId>
|
||||
<artifactId>language-server-starter</artifactId>
|
||||
<version>${dependencies.version}</version>
|
||||
</dependency>
|
||||
<!-- other -->
|
||||
<dependency>
|
||||
<!-- Local modified JSON lib packaged to support order in maps -->
|
||||
<groupId>org.springframework.ide.eclipse</groupId>
|
||||
@@ -62,11 +68,6 @@
|
||||
<artifactId>commons-boot-app-cli</artifactId>
|
||||
<version>${dependencies.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<version>${boot-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jdt</groupId>
|
||||
<artifactId>org.eclipse.jdt.core</artifactId>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 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;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
|
||||
@SpringBootApplication
|
||||
public class BootLanguagServerBootApp {
|
||||
private static final String SERVER_NAME = "boot-language-server";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
LogRedirect.bootRedirectToFile(SERVER_NAME); //TODO: use boot (or logback realy) to configure logging instead.
|
||||
SpringApplication.run(BootLanguagServerBootApp.class, args);
|
||||
}
|
||||
|
||||
@Bean public String serverName() {
|
||||
return SERVER_NAME;
|
||||
}
|
||||
|
||||
@Bean SimpleLanguageServer languageServer() {
|
||||
return BootLanguageServer.create(BootLanguageServerParams.createDefault()).getServer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 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;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp;
|
||||
import org.springframework.ide.vscode.commons.util.LogRedirect;
|
||||
|
||||
/**
|
||||
* Starts up Language Server process
|
||||
*
|
||||
* @author Alex Boyko
|
||||
* @author Kris De Volder
|
||||
*
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
String serverName = "boot-language-server";
|
||||
LogRedirect.redirectToFile(serverName);
|
||||
LaunguageServerApp.start(serverName,
|
||||
() -> BootLanguageServer.create(BootLanguageServerParams.createDefault()).getServer()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!-- encoders are assigned the type
|
||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
|
||||
<target>System.err</target>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -126,7 +126,8 @@ export function activate(options: ActivatorOptions, context: VSCode.ExtensionCon
|
||||
'-Dspring.lsp.client-port='+port,
|
||||
'-Dserver.port=' + port,
|
||||
'-Dsts.lsp.client=vscode',
|
||||
'-Dsts.log.file=' + logfile
|
||||
'-Dsts.log.file=' + logfile, //old style log redirect
|
||||
'-Dlogging.file=' + logfile // spring boot log redirect
|
||||
];
|
||||
if (options.checkjvm) {
|
||||
options.checkjvm(context, jvm);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
#if [ ! -d "node_modules/commons-vscode" ]; then
|
||||
./scripts/preinstall.sh
|
||||
#fi
|
||||
npm install
|
||||
npm run vsce-package
|
||||
npm run vsce-package
|
||||
rm -fr ~/.vscode/extensions/pivotal.vscode-concourse*
|
||||
rm -fr ~/.vscode/extensions/.obsolete
|
||||
code --install-extension vscode-concourse-*.vsix
|
||||
7
vscode-extensions/vscode-manifest-yaml/build.sh
Executable file
7
vscode-extensions/vscode-manifest-yaml/build.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
npm install
|
||||
npm run vsce-package
|
||||
rm -fr ~/.vscode/extensions/pivotal.vscode-manifest-yaml*
|
||||
rm -fr ~/.vscode/extensions/.obsolete
|
||||
code --install-extension vscode-*.vsix
|
||||
Reference in New Issue
Block a user