Skip to content

Commit

Permalink
Add language server
Browse files Browse the repository at this point in the history
  • Loading branch information
anonl committed Aug 22, 2020
1 parent 4d91a74 commit 556e3f3
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -3,6 +3,7 @@ template/save
android/libs
.vscode
save
*.log

## Java

Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/nl/weeaboo/vn/impl/InitConfig.java
Expand Up @@ -29,6 +29,18 @@ public void uncaughtException(Thread t, Throwable e) {

private static void configLogging() {
System.setProperty("sun.io.serialization.extendedDebugInfo", "true");

try {
Class<?> bridgeHandler = Class.forName("org.slf4j.bridge.SLF4JBridgeHandler");
try {
bridgeHandler.getMethod("removeHandlersForRootLogger").invoke(null);
bridgeHandler.getMethod("install").invoke(null);
} catch (ReflectiveOperationException | SecurityException e) {
LOG.debug("Unexpected exception while trying to install JUL->SLF4J bridge", e);
}
} catch (ClassNotFoundException cnfe) {
LOG.debug("No JUL->SLF4J bridge found; java.util.logging may not be included in the log output", cnfe);
}
}

}
7 changes: 6 additions & 1 deletion dependencies.gradle
Expand Up @@ -13,6 +13,7 @@ def gdxTestVersion = '2.0.2'
def gdxVersion = '1.9.8'
def gdxVideoVersion = '1.0.0-anonl'
def guavaVersion = '23.6-android' // Note: The -android version is not Android-specific, only Android-compatible
def lsp4jVersion = '0.9.0'
def roboVmVersion = '2.3.0'
def slf4jVersion = '1.7.21'
def styledTextVersion = '4.0.0'
Expand Down Expand Up @@ -110,7 +111,11 @@ deps.junit = [
]

deps.lsp4j = [
'org.eclipse.lsp4j:org.eclipse.lsp4j.debug:0.9.0'
'org.eclipse.lsp4j:org.eclipse.lsp4j:' + lsp4jVersion
]

deps.lsp4j_debug = [
'org.eclipse.lsp4j:org.eclipse.lsp4j.debug:' + lsp4jVersion
]

deps.lua_core = [
Expand Down
7 changes: 7 additions & 0 deletions langserver/.checkstyle
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>

<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
<fileset name="all" enabled="true" check-config-name="NVList" local="false">
<file-match-pattern match-pattern="." include-pattern="true"/>
</fileset>
</fileset-config>
47 changes: 47 additions & 0 deletions langserver/build.gradle
@@ -0,0 +1,47 @@

plugins {
id 'java-library'
id 'application'
id 'maven-publish'
id 'com.github.johnrengelman.shadow'
}

mainClassName = "nl.weeaboo.vn.langserver.LangServerMain"

dependencies {
implementation project(':nvlist-core')
implementation deps.lsp4j

runtimeOnly deps.slf4j_desktop
}

run {
standardInput = System.in
}

tasks.register('runLangServer') {
dependsOn 'run'
}

shadowJar {
baseName = 'nvlist-langserver'
classifier = null
version = null
}

publishing {
publications {
langserver(MavenPublication) {
from components.java
artifactId 'nvlist-langserver'

pom.withXml {
asNode().children().last() + pomLicenseConfig
}
}
}
}

bintray {
publications = ['langserver']
}
@@ -0,0 +1,15 @@
package nl.weeaboo.vn.langserver;

import java.io.IOException;

import nl.weeaboo.vn.impl.InitConfig;

final class LangServerMain {

public static void main(String[] args) throws IOException {
InitConfig.init();

NvlistLangServer.start(System.in, System.out);
}

}
@@ -0,0 +1,101 @@
package nl.weeaboo.vn.langserver;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.eclipse.lsp4j.jsonrpc.Launcher;
import org.eclipse.lsp4j.launch.LSPLauncher;
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;

/**
* Main language server protocol implementation for NVList.
*/
public final class NvlistLangServer implements LanguageServer, LanguageClientAware, Closeable {

private static final Logger LOG = LoggerFactory.getLogger(NvlistLangServer.class);

private final NvlistTextDocumentService textDocumentService = new NvlistTextDocumentService();
private final NvlistWorkspaceService workspaceService = new NvlistWorkspaceService();
private Future<Void> messageHandler;

public static void start(InputStream in, OutputStream out) throws IOException {
LOG.info("Starting NVList language server...");

NvlistLangServer server = new NvlistLangServer();
Launcher<LanguageClient> launcher = new LSPLauncher.Builder<LanguageClient>()
.setLocalService(server)
.setRemoteInterface(LanguageClient.class)
.setInput(in)
.setOutput(out)
.traceMessages(new PrintWriter(Files.newBufferedWriter(Paths.get("nvlist-langserver-trace.log"))))
.create();
LanguageClient peer = launcher.getRemoteProxy();
server.connect(peer);
server.messageHandler = launcher.startListening();
}

@Override
public void connect(LanguageClient client) {
textDocumentService.connect(client);
}


@Override
public void close() {
LOG.info("Closing NVList language server...");

messageHandler.cancel(true);
}

@Override
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
LOG.info("Received initialize request ({})", params);

ServerCapabilities caps = new ServerCapabilities();
caps.setTextDocumentSync(TextDocumentSyncKind.Full);
caps.setDefinitionProvider(true);

InitializeResult result = new InitializeResult();
result.setCapabilities(caps);
return CompletableFuture.completedFuture(result);
}

@Override
public CompletableFuture<Object> shutdown() {
LOG.info("Received shutdown request");

// TODO: Implement
return CompletableFuture.completedFuture(null);
}

@Override
public void exit() {
close();
}

@Override
public NvlistTextDocumentService getTextDocumentService() {
return textDocumentService;
}

@Override
public NvlistWorkspaceService getWorkspaceService() {
return workspaceService;
}

}
@@ -0,0 +1,89 @@
package nl.weeaboo.vn.langserver;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.eclipse.lsp4j.DefinitionParams;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.LocationLink;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.lsp4j.services.LanguageClient;
import org.eclipse.lsp4j.services.LanguageClientAware;
import org.eclipse.lsp4j.services.TextDocumentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import nl.weeaboo.vn.core.NovelPrefs;
import nl.weeaboo.vn.impl.script.lvn.ILvnParser;
import nl.weeaboo.vn.impl.script.lvn.LvnParserFactory;

final class NvlistTextDocumentService implements TextDocumentService, LanguageClientAware {

private static final Logger LOG = LoggerFactory.getLogger(NvlistTextDocumentService.class);

@SuppressWarnings("unused")
private LanguageClient peer;

NvlistTextDocumentService() {
}

@Override
public void connect(LanguageClient client) {
this.peer = client;
}

private static ILvnParser getLvnParser() {
// TODO: Use actual engine target version based on config
return LvnParserFactory.getParser(NovelPrefs.ENGINE_TARGET_VERSION.getDefaultValue());
}

@Override
public void didOpen(DidOpenTextDocumentParams params) {
LOG.debug("didOpen({})", params);

TextDocumentItem doc = params.getTextDocument();

TODO: Modify LvnParser to return (optionally) a full AST instead of just a string
getLvnParser().parseFile(filename, in)
new LvnParserFactory().getParser(engineVersion).getText();
}

@Override
public void didChange(DidChangeTextDocumentParams params) {
LOG.debug("didChange({})", params);
}

@Override
public void didClose(DidCloseTextDocumentParams params) {
LOG.debug("didClose({})", params);
}

@Override
public void didSave(DidSaveTextDocumentParams params) {
LOG.debug("didSave({})", params);
}

@Override
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> definition(
DefinitionParams params) {

List<Location> locations = new ArrayList<>();

Location loc = new Location();
// TODO: Implement properly
loc.setUri(params.getTextDocument().getUri());
loc.setRange(new Range(new Position(0, 0), new Position(0, 10)));
locations.add(loc);

return CompletableFuture.completedFuture(Either.forLeft(locations));
}

}
@@ -0,0 +1,26 @@
package nl.weeaboo.vn.langserver;

import org.eclipse.lsp4j.DidChangeConfigurationParams;
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
import org.eclipse.lsp4j.services.WorkspaceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

final class NvlistWorkspaceService implements WorkspaceService {

private static final Logger LOG = LoggerFactory.getLogger(NvlistWorkspaceService.class);

NvlistWorkspaceService() {
}

@Override
public void didChangeConfiguration(DidChangeConfigurationParams params) {
LOG.debug("disChangeConfiguration({})", params);
}

@Override
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
LOG.debug("didChangeWatchedFiles({})", params);
}

}
13 changes: 13 additions & 0 deletions langserver/src/main/resources/logback.xml
@@ -0,0 +1,13 @@
<configuration>
<appender name="file" class="ch.qos.logback.core.FileAppender">
<file>nvlist-langserver.log</file>
<append>false</append>
<encoder>
<pattern>| %-5level | %msg | %file:%line | %thread%n</pattern>
</encoder>
</appender>

<root level="debug">
<appender-ref ref="file" />
</root>
</configuration>
2 changes: 2 additions & 0 deletions settings.gradle
Expand Up @@ -13,6 +13,7 @@ def subProjects = [
'docs',
'buildtools',
'buildgui',
'langserver',
]

subProjects.each { proj ->
Expand All @@ -22,3 +23,4 @@ subProjects.each { proj ->

// includeBuild '../tcommon'
// includeBuild '../gdx-styledtext'
includeBuild '../luajpp2'

0 comments on commit 556e3f3

Please sign in to comment.