From c509f2ac54d2a96c55e79f3e112397a0c66c49d4 Mon Sep 17 00:00:00 2001 From: Nikita Gryzlov Date: Sat, 17 Nov 2018 22:13:20 +0700 Subject: [PATCH 1/2] Rewrite TestServer in Java --- .idea/compiler.xml | 2 + build.gradle | 6 + settings.gradle | 1 + .../lsp/server => resources}/test1.test | 0 .../lsp/server => resources}/test2.test | 0 .../github/gtache/lsp/server/TestServer.scala | 330 -------------- testServer/build.gradle | 28 ++ .../github/gtache/lsp/server/TestServer.java | 406 ++++++++++++++++++ 8 files changed, 443 insertions(+), 330 deletions(-) rename src/test/{scala/com/github/gtache/lsp/server => resources}/test1.test (100%) rename src/test/{scala/com/github/gtache/lsp/server => resources}/test2.test (100%) delete mode 100644 src/test/scala/com/github/gtache/lsp/server/TestServer.scala create mode 100644 testServer/build.gradle create mode 100644 testServer/src/main/java/com/github/gtache/lsp/server/TestServer.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 6f84bc2..71a3f94 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -4,6 +4,8 @@ + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 0747546..29a1be4 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,12 @@ test { finalizedBy jacocoTestReport } +//compileTestJava.dependsOn(compileTestScala) + +task testJar(type: Jar) { + from sourceSets.test.output +} + // IDE run and debugging runIde { diff --git a/settings.gradle b/settings.gradle index af6b405..35c9780 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ rootProject.name = 'LSP' +include 'testServer' \ No newline at end of file diff --git a/src/test/scala/com/github/gtache/lsp/server/test1.test b/src/test/resources/test1.test similarity index 100% rename from src/test/scala/com/github/gtache/lsp/server/test1.test rename to src/test/resources/test1.test diff --git a/src/test/scala/com/github/gtache/lsp/server/test2.test b/src/test/resources/test2.test similarity index 100% rename from src/test/scala/com/github/gtache/lsp/server/test2.test rename to src/test/resources/test2.test diff --git a/src/test/scala/com/github/gtache/lsp/server/TestServer.scala b/src/test/scala/com/github/gtache/lsp/server/TestServer.scala deleted file mode 100644 index cecd660..0000000 --- a/src/test/scala/com/github/gtache/lsp/server/TestServer.scala +++ /dev/null @@ -1,330 +0,0 @@ -/** - * Copyright 2017-2018 Guillaume Tâche - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.github.gtache.lsp.server - -import java.io.File -import java.util -import java.util.concurrent.CompletableFuture - -import org.eclipse.lsp4j._ -import org.eclipse.lsp4j.jsonrpc.messages -import org.eclipse.lsp4j.jsonrpc.messages.Either -import org.eclipse.lsp4j.launch.LSPLauncher -import org.eclipse.lsp4j.services._ - -/** - * Simple server responding with constant responses/edits - */ -class TestServer extends LanguageServer with LanguageClientAware with TextDocumentService with WorkspaceService { - - import scala.collection.JavaConverters._ - - private val pos0 = new Position(0, 0) - private val range0 = new Range(pos0, pos0) - private val commands = Array("command1", "command2", "command3") - private val completionItems = Array( - { - val a = new CompletionItem("A") - a.setInsertText("CompletionA") - a.setInsertTextFormat(InsertTextFormat.PlainText) - a.setKind(CompletionItemKind.Enum) - a.setDetail("DetailA") - a.setDocumentation("DocumentationA") - a - }, { - val b = new CompletionItem("B") - b.setTextEdit(new TextEdit(range0, "CompletedB")) - b.setAdditionalTextEdits(createList(new TextEdit(new Range(new Position(0, 20), new Position(0, 30)), "CompletedB"))) - b.setKind(CompletionItemKind.Class) - b.setDetail("DetailB") - b - }, { - val c = new CompletionItem("C") - c.setCommand(new Command(commands(0), commands(0))) - c.setDocumentation("DocumentationC") - c - }, { - val d = new CompletionItem("D") - d.setKind(CompletionItemKind.Method) - d - } - ) - private val uris = List(new File("D:\\Projects\\Scala\\DottyExample\\src\\main\\test1.test").toURI.toString, new File("D:\\Projects\\Scala\\DottyExample\\src\\main\\test2.test").toURI.toString) - private val capabilities: ServerCapabilities = new ServerCapabilities() - private val syncKindOptions = new TextDocumentSyncOptions - private val positions: IndexedSeq[Position] = IndexedSeq(pos0, new Position(0, 10), new Position(0, 20), new Position(0, 30), - new Position(1, 0), new Position(1, 10), new Position(1, 20), new Position(1, 30)) - capabilities.setCodeActionProvider(true) - capabilities.setCodeLensProvider(new CodeLensOptions(true)) - capabilities.setCompletionProvider(new CompletionOptions(true, createList("^"))) - capabilities.setDefinitionProvider(true) - capabilities.setDocumentFormattingProvider(true) - capabilities.setDocumentHighlightProvider(true) - capabilities.setDocumentLinkProvider(new DocumentLinkOptions(true)) - capabilities.setDocumentOnTypeFormattingProvider(new DocumentOnTypeFormattingOptions("°", createList("§"))) - capabilities.setDocumentRangeFormattingProvider(true) - capabilities.setDocumentSymbolProvider(true) - capabilities.setExecuteCommandProvider(new ExecuteCommandOptions(commands.toIndexedSeq.asJava)) - capabilities.setHoverProvider(true) - capabilities.setReferencesProvider(true) - capabilities.setRenameProvider(true) - capabilities.setSignatureHelpProvider(new SignatureHelpOptions(createList("("))) - private val ranges: IndexedSeq[Range] = positions.zip(positions.tail).map(pos => new Range(pos._1, pos._2)) - syncKindOptions.setChange(TextDocumentSyncKind.Incremental) - syncKindOptions.setOpenClose(true) - syncKindOptions.setSave(new SaveOptions(true)) - syncKindOptions.setWillSave(true) - syncKindOptions.setWillSaveWaitUntil(true) - capabilities.setTextDocumentSync(syncKindOptions) - capabilities.setWorkspaceSymbolProvider(true) - private var client: LanguageClient = _ - private var willSaveWaitUntilCalled = false - - override def symbol(params: WorkspaceSymbolParams): CompletableFuture[util.List[_ <: SymbolInformation]] = { - val symbols = Array( - new SymbolInformation("SymbA", SymbolKind.Class, new Location(uris.head, ranges(4))), - new SymbolInformation("SymbB", SymbolKind.Enum, new Location(uris.tail.head, ranges(2))) - ) - if (params.getQuery.isEmpty) CompletableFuture.completedFuture(symbols.toIndexedSeq.asJava) else CompletableFuture.completedFuture(createList(symbols.head)) - } - - override def didChangeWatchedFiles(params: DidChangeWatchedFilesParams): Unit = { - client.logMessage(new MessageParams(MessageType.Info, "DidChangeWatchedFiles " + params)) - } - - override def didChangeConfiguration(params: DidChangeConfigurationParams): Unit = { - client.logMessage(new MessageParams(MessageType.Info, "didChangeConfiguration " + params)) - } - - override def references(params: ReferenceParams): CompletableFuture[util.List[_ <: Location]] = { - val uri = params.getTextDocument.getUri - if (uri.contains("test1")) { - CompletableFuture.completedFuture(createList(new Location(uri, ranges(1)), new Location(uri, ranges(4)))) - } else { - CompletableFuture.completedFuture(createList(new Location(uris.tail.head, ranges(3)))) - } - } - - override def resolveCompletionItem(unresolved: CompletionItem): CompletableFuture[CompletionItem] = { - CompletableFuture.completedFuture(unresolved) - } - - override def codeLens(params: CodeLensParams): CompletableFuture[util.List[_ <: CodeLens]] = { - CompletableFuture.completedFuture(createList(new CodeLens(ranges(1), new Command(commands(0), commands(0)), null))) - } - - private def createList[T](t: T*): util.List[T] = { - t.toIndexedSeq.asJava - } - - override def documentHighlight(position: TextDocumentPositionParams): CompletableFuture[util.List[_ <: DocumentHighlight]] = { - CompletableFuture.completedFuture(createList(new DocumentHighlight(ranges(1), DocumentHighlightKind.Read), - new DocumentHighlight(ranges(4), DocumentHighlightKind.Write), - new DocumentHighlight(ranges(2), DocumentHighlightKind.Text))) - } - - override def didChange(params: DidChangeTextDocumentParams): Unit = { - client.publishDiagnostics(new PublishDiagnosticsParams(uris.head, createList( - new Diagnostic(new Range(new Position(0, 0), new Position(0, 20)), "Warning", DiagnosticSeverity.Warning, "TestServer", "code0"), - new Diagnostic(ranges(3), "Error", DiagnosticSeverity.Error, "Also TestServer"), - new Diagnostic(ranges(4), "Info", DiagnosticSeverity.Information, null, "code1"), - new Diagnostic(ranges(5), "Hint", DiagnosticSeverity.Hint, null, null - )))) - } - - override def hover(position: TextDocumentPositionParams): CompletableFuture[Hover] = { - val uri = position.getTextDocument.getUri - if (uri.contains("test1")) { - CompletableFuture.completedFuture(new Hover(createList(Either.forRight(new MarkedString(null, "**Bold** *Italic*"))).asInstanceOf[util.List[Either[String, MarkedString]]], ranges(1))) - } else { - CompletableFuture.completedFuture(new Hover(createList(Either.forLeft("This is hover")).asInstanceOf[util.List[Either[String, MarkedString]]])) - } - } - - override def documentSymbol(params: DocumentSymbolParams): CompletableFuture[util.List[_ <: SymbolInformation]] = { - val uri = params.getTextDocument.getUri - if (uri.contains("test1")) { - val symbols = Array( - new SymbolInformation("SymbA", SymbolKind.Class, new Location(uri, ranges(1))), - new SymbolInformation("SymbB", SymbolKind.Enum, new Location(uri, ranges(4))) - ) - CompletableFuture.completedFuture(symbols.toIndexedSeq.asJava) - } else { - val nUri = uris.tail.head - val symbols = Array( - new SymbolInformation("SymbA", SymbolKind.Class, new Location(nUri, ranges(1))), - new SymbolInformation("SymbB", SymbolKind.Enum, new Location(nUri, ranges(4))) - ) - CompletableFuture.completedFuture(symbols.toIndexedSeq.asJava) - } - } - - override def didClose(params: DidCloseTextDocumentParams): Unit = { - client.logMessage(new MessageParams(MessageType.Info, "DidClose")) - } - - override def didSave(params: DidSaveTextDocumentParams): Unit = { - client.logMessage(new MessageParams(MessageType.Info, "DidSave")) - } - - override def definition(position: TextDocumentPositionParams): CompletableFuture[util.List[_ <: Location]] = { - val uri = position.getTextDocument.getUri - if (uri.contains("test1")) { - CompletableFuture.completedFuture(createList(new Location(uri, ranges(1)))) - } else { - CompletableFuture.completedFuture(createList(new Location(uris.tail.head, ranges(4)))) - } - } - - override def resolveCodeLens(unresolved: CodeLens): CompletableFuture[CodeLens] = { - CompletableFuture.completedFuture(unresolved) - } - - - override def completion(position: CompletionParams): CompletableFuture[messages.Either[util.List[CompletionItem], CompletionList]] = { - CompletableFuture.completedFuture(Either.forRight(new CompletionList(completionItems.toIndexedSeq.asJava))) - } - - override def onTypeFormatting(params: DocumentOnTypeFormattingParams): CompletableFuture[util.List[_ <: TextEdit]] = { - CompletableFuture.completedFuture(createList(new TextEdit(range0, "Formatted"))) - } - - override def didOpen(params: DidOpenTextDocumentParams): Unit = { - - } - - override def signatureHelp(position: TextDocumentPositionParams): CompletableFuture[SignatureHelp] = { - val uri = position.getTextDocument.getUri - if (uri.contains("test1")) { - CompletableFuture.completedFuture(new SignatureHelp( - createList( - new SignatureInformation("SigA(paramA : String, paramB : Int = 2)", "Do A", createList( - new ParameterInformation("paramA", "paramA000"), - new ParameterInformation("paramB", "paramB001") - )), - new SignatureInformation("SigB(paramA : String)", "Do B", createList( - new ParameterInformation("paramA", "paramA010") - )) - ), 0, 1)) - } else { - CompletableFuture.completedFuture(new SignatureHelp( - createList( - new SignatureInformation("SigAA(paramAA : String, paramBB : String = \"\")", "Does AA", createList( - new ParameterInformation("paramAA", "paramAA100"), - new ParameterInformation("paramBB", "paramBB101") - )), - new SignatureInformation("SigBB(paramBB : Int)", "Does BB", createList( - new ParameterInformation("paramBB", "paramBB110") - )) - ) - , 1, 0)) - } - } - - override def documentLink(params: DocumentLinkParams): CompletableFuture[util.List[DocumentLink]] = super.documentLink(params) - - override def documentLinkResolve(params: DocumentLink): CompletableFuture[DocumentLink] = super.documentLinkResolve(params) - - override def willSave(params: WillSaveTextDocumentParams): Unit = super.willSave(params) - - override def executeCommand(params: ExecuteCommandParams): CompletableFuture[AnyRef] = { - val title = params.getCommand - if (title == commands(0)) { - client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(scala.collection.mutable.Map(uris.head -> createList(new TextEdit(range0, "Command0"))).asJava))) - CompletableFuture.completedFuture(new Object) - } else { - CompletableFuture.completedFuture(new WorkspaceEdit(scala.collection.mutable.Map(uris.head -> createList(new TextEdit(range0, "Command1or2"))).asJava)) - } - } - - override def willSaveWaitUntil(params: WillSaveTextDocumentParams): CompletableFuture[util.List[TextEdit]] = { - if (!willSaveWaitUntilCalled) { - willSaveWaitUntilCalled = true - CompletableFuture.completedFuture(createList(new TextEdit(range0, "WillSaveWaitUntil"))) - } else { - CompletableFuture.completedFuture(createList()) - } - } - - @SuppressWarnings(Array("deprecation")) - override def initialized(): Unit = super.initialized() - - override def rangeFormatting(params: DocumentRangeFormattingParams): CompletableFuture[util.List[_ <: TextEdit]] = { - val range = params.getRange - val length = range.getEnd.getCharacter - range.getStart.getCharacter - CompletableFuture.completedFuture(createList(new TextEdit(range, "f" * length))) - } - - override def codeAction(params: CodeActionParams): CompletableFuture[util.List[_ <: Command]] = { - if (params.getRange.getEnd.getCharacter < 25 && params.getRange.getEnd.getLine < 1) { - CompletableFuture.completedFuture(createList(new Command(commands(0), commands(0)))) - } else { - CompletableFuture.completedFuture(createList(new Command(commands(1), commands(1)), new Command(commands(2), commands(2)))) - } - } - - override def rename(params: RenameParams): CompletableFuture[WorkspaceEdit] = { - val req = client.showMessageRequest(new ShowMessageRequestParams(createList( - new MessageActionItem("One"), - new MessageActionItem("Two"), - new MessageActionItem("Three") - ))) - req.thenAccept(item => client.showMessage(new MessageParams(MessageType.Info, "Chose " + item.getTitle))) - CompletableFuture.completedFuture(new WorkspaceEdit(scala.collection.mutable.Map( - params.getTextDocument.getUri -> createList(new TextEdit(ranges(1), "Renamed"))).asJava)) - } - - override def formatting(params: DocumentFormattingParams): CompletableFuture[util.List[_ <: TextEdit]] = { - val uri = params.getTextDocument.getUri - if (uri.contains("test1")) { - CompletableFuture.completedFuture(createList(new TextEdit(range0, "Formatted"))) - } else { - CompletableFuture.completedFuture(createList(new TextEdit(ranges(1), ""))) - } - } - - override def getTextDocumentService: TextDocumentService = this - - override def exit(): Unit = { - System.exit(0) - } - - override def initialize(params: InitializeParams): CompletableFuture[InitializeResult] = { - CompletableFuture.completedFuture(new InitializeResult(capabilities)) - } - - override def connect(client: LanguageClient): Unit = this.client = client - - override def getWorkspaceService: WorkspaceService = this - - override def shutdown(): CompletableFuture[AnyRef] = CompletableFuture.completedFuture(new Object) - - override def initialized(params: InitializedParams): Unit = { - } -} - -object TestServer { - def main(args: Array[String]): Unit = { - val in = System.in - val out = System.out - val server = new TestServer - val launcher = LSPLauncher.createServerLauncher(server, in, out) - val client = launcher.getRemoteProxy - server.connect(client) - launcher.startListening() - System.err.println("Listening") - } -} \ No newline at end of file diff --git a/testServer/build.gradle b/testServer/build.gradle new file mode 100644 index 0000000..c34817e --- /dev/null +++ b/testServer/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java' +} + +repositories { + mavenCentral() +} + +dependencies { + compile group: 'org.eclipse.lsp4j', name: 'org.eclipse.lsp4j', version: '0.4.1' + // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 + compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' +} + +jar { + manifest { + attributes 'Main-Class': 'com.github.gtache.lsp.server.TestServer' + } + from (configurations.compile.collect { entry -> zipTree(entry) }) { + exclude 'META-INF/MANIFEST.MF' + exclude 'META-INF/*.SF' + exclude 'META-INF/*.DSA' + exclude 'META-INF/*.RSA' + } +} + +apply plugin: 'java' + diff --git a/testServer/src/main/java/com/github/gtache/lsp/server/TestServer.java b/testServer/src/main/java/com/github/gtache/lsp/server/TestServer.java new file mode 100644 index 0000000..9f77004 --- /dev/null +++ b/testServer/src/main/java/com/github/gtache/lsp/server/TestServer.java @@ -0,0 +1,406 @@ +package com.github.gtache.lsp.server; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.lsp4j.*; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +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.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4j.services.WorkspaceService; + +import java.io.File; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class TestServer + implements LanguageServer, + LanguageClientAware, + TextDocumentService, + WorkspaceService { + private final Position pos0 = new Position(0, 0); + private final Range range0 = new Range(this.pos0, this.pos0); + private final String[] commands = new String[]{"command1", "command2", "command3"}; + private final CompletionItem[] completionItems; + private final List uris; + private final ServerCapabilities capabilities; + private final TextDocumentSyncOptions syncKindOptions; + private final Position[] positions; + private final Range[] ranges; + private LanguageClient client; + private boolean willSaveWaitUntilCalled; + + @JsonNotification + public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) { + WorkspaceService.super.didChangeWorkspaceFolders(params); + } + + @JsonRequest + public CompletableFuture> typeDefinition(TextDocumentPositionParams position) { + return TextDocumentService.super.typeDefinition(position); + } + + @JsonRequest + public CompletableFuture> implementation(TextDocumentPositionParams position) { + return TextDocumentService.super.implementation(position); + } + + @JsonRequest + public CompletableFuture> documentColor(DocumentColorParams params) { + return TextDocumentService.super.documentColor(params); + } + + @JsonRequest + public CompletableFuture> colorPresentation(ColorPresentationParams params) { + return TextDocumentService.super.colorPresentation(params); + } + + private void setClient(LanguageClient client) { + this.client = client; + } + + private boolean willSaveWaitUntilCalled() { + return this.willSaveWaitUntilCalled; + } + + private void setWillSaveWaitUntilCalled(boolean willSaveWaitUntilCalled) { + this.willSaveWaitUntilCalled = willSaveWaitUntilCalled; + } + + public CompletableFuture> symbol(WorkspaceSymbolParams params) { + SymbolInformation[] symbols = new SymbolInformation[]{ + new SymbolInformation("SymbA", SymbolKind.Class, new Location(this.uris.get(0), this.ranges[4])), + new SymbolInformation("SymbB", SymbolKind.Enum, new Location(this.uris.get(0), this.ranges[2])) + }; + return params.getQuery().isEmpty() + ? CompletableFuture.completedFuture(Arrays.asList(symbols)) + : CompletableFuture.completedFuture(Collections.singletonList(symbols[0])); + } + + public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { + this.client.logMessage(new MessageParams(MessageType.Info, "DidChangeWatchedFiles " + params)); + } + + public void didChangeConfiguration(DidChangeConfigurationParams params) { + this.client.logMessage(new MessageParams(MessageType.Info, "didChangeConfiguration " + params)); + } + + public CompletableFuture> references(ReferenceParams params) { + String uri = params.getTextDocument().getUri(); + return uri.contains("test1") + ? CompletableFuture.completedFuture(Arrays.asList(new Location(uri, ranges[1]), new Location(uri, ranges[4]))) + : CompletableFuture.completedFuture(Collections.singletonList(new Location(uris.get(uris.size() - 1), ranges[3]))); + } + + public CompletableFuture resolveCompletionItem(CompletionItem unresolved) { + return CompletableFuture.completedFuture(unresolved); + } + + public CompletableFuture> codeLens(CodeLensParams params) { + return CompletableFuture.completedFuture(Collections.singletonList(new CodeLens(ranges[0], new Command(this.commands[0], this.commands[0]), null))); + } + + public CompletableFuture> documentHighlight(TextDocumentPositionParams position) { + return CompletableFuture.completedFuture(Arrays.asList(new DocumentHighlight(this.ranges[1], DocumentHighlightKind.Read), + new DocumentHighlight(this.ranges[4], DocumentHighlightKind.Write), + new DocumentHighlight(this.ranges[2], DocumentHighlightKind.Text))); + } + + public void didChange(DidChangeTextDocumentParams params) { + this.client.publishDiagnostics(new PublishDiagnosticsParams(this.uris.get(0), Arrays.asList( + new Diagnostic(new Range(new Position(0, 0), new Position(0, 20)), "Warning", DiagnosticSeverity.Warning, "TestServer", "code0"), + new Diagnostic(this.ranges[3], "Error", DiagnosticSeverity.Error, "Also TestServer"), + new Diagnostic(this.ranges[4], "Info", DiagnosticSeverity.Information, null, "code1"), + new Diagnostic(this.ranges[5], "Hint", DiagnosticSeverity.Hint, null, null))) + ); + } + + public CompletableFuture hover(TextDocumentPositionParams position) { + String uri = position.getTextDocument().getUri(); + return uri.contains("test1") + ? CompletableFuture.completedFuture(new Hover(Collections.singletonList(Either.forRight(new MarkedString(null, "**Bold** *Italic*"))))) + : CompletableFuture.completedFuture(new Hover(Collections.singletonList(Either.forLeft("This is hover")))); + } + + public CompletableFuture> documentSymbol(DocumentSymbolParams params) { + CompletableFuture> completableFuture; + String uri = params.getTextDocument().getUri(); + if (uri.contains("test1")) { + SymbolInformation[] symbols = new SymbolInformation[]{ + new SymbolInformation("SymbA", SymbolKind.Class, new Location(uri, this.ranges[1])), + new SymbolInformation("SymbB", SymbolKind.Enum, new Location(uri, this.ranges[4])) + }; + completableFuture = CompletableFuture.completedFuture(Arrays.asList(symbols)); + } else { + String nUri = this.uris.get(this.uris.size() - 1); + SymbolInformation[] symbols = new SymbolInformation[]{ + new SymbolInformation("SymbA", SymbolKind.Class, new Location(nUri, this.ranges[1])), + new SymbolInformation("SymbB", SymbolKind.Enum, new Location(nUri, this.ranges[4])) + }; + completableFuture = CompletableFuture.completedFuture(Arrays.asList(symbols)); + } + return completableFuture; + } + + public void didClose(DidCloseTextDocumentParams params) { + this.client.logMessage(new MessageParams(MessageType.Info, "DidClose")); + } + + public void didSave(DidSaveTextDocumentParams params) { + this.client.logMessage(new MessageParams(MessageType.Info, "DidSave")); + } + + public CompletableFuture> definition(TextDocumentPositionParams position) { + String uri = position.getTextDocument().getUri(); + return uri.contains("test1") + ? CompletableFuture.completedFuture(Collections.singletonList(new Location(uri, this.ranges[1]))) + : CompletableFuture.completedFuture(Collections.singletonList(new Location(this.uris.get(this.uris.size() - 1), this.ranges[4]))); + } + + public CompletableFuture resolveCodeLens(CodeLens unresolved) { + return CompletableFuture.completedFuture(unresolved); + } + + public CompletableFuture, CompletionList>> completion(CompletionParams position) { + return CompletableFuture.completedFuture(Either.forRight(new CompletionList(Arrays.asList(completionItems)))); + } + + public CompletableFuture> onTypeFormatting(DocumentOnTypeFormattingParams params) { + return CompletableFuture.completedFuture(Collections.singletonList(new TextEdit(this.range0, "Formatted"))); + } + + public void didOpen(DidOpenTextDocumentParams params) { + } + + public CompletableFuture signatureHelp(TextDocumentPositionParams position) { + String uri = position.getTextDocument().getUri(); + if (uri.contains("test1")) + return CompletableFuture.completedFuture(new SignatureHelp( + Arrays.asList( + new SignatureInformation("SigA(paramA : String, paramB : Int = 2)", "Do A", Arrays.asList( + new ParameterInformation("paramA", "paramA000"), + new ParameterInformation("paramB", "paramB001")) + ), + new SignatureInformation("SigB(paramA : String)", "Do B", Collections.singletonList( + new ParameterInformation("paramA", "paramA010")) + ) + ), + 0, + 1 + ) + ); + else + return CompletableFuture.completedFuture(new SignatureHelp( + Arrays.asList( + new SignatureInformation( + "SigAA(paramAA : String, paramBB : String = \"\")", + "Does AA", + Arrays.asList( + new ParameterInformation("paramAA", "paramAA100"), + new ParameterInformation("paramBB", "paramBB101") + ) + ), + new SignatureInformation( + "SigBB(paramBB : Int)", + "Does BB", + Collections.singletonList( + new ParameterInformation("paramBB", "paramBB110") + ) + ) + ), + 1, + 0 + ) + ); + } + + public CompletableFuture> documentLink(DocumentLinkParams params) { + return TextDocumentService.super.documentLink(params); + } + + public CompletableFuture documentLinkResolve(DocumentLink params) { + return TextDocumentService.super.documentLinkResolve(params); + } + + public void willSave(WillSaveTextDocumentParams params) { + TextDocumentService.super.willSave(params); + } + + public CompletableFuture executeCommand(ExecuteCommandParams params) { + CompletableFuture completableFuture; + String title = params.getCommand(); + if (Objects.equals(title, this.commands[0])) { + Map> changes = new HashMap<>(); + changes.put( + uris.get(0), + Collections.singletonList( + new TextEdit(range0, "Command0") + ) + ); + this.client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(changes))); + completableFuture = CompletableFuture.completedFuture(new Object()); + } else { + Map> changes = new HashMap<>(); + changes.put( + uris.get(0), + Collections.singletonList( + new TextEdit(range0, "Command1or2") + ) + ); + completableFuture = CompletableFuture.completedFuture(new WorkspaceEdit(changes)); + } + return completableFuture; + } + + public CompletableFuture> willSaveWaitUntil(WillSaveTextDocumentParams params) { + CompletableFuture> completableFuture; + if (!this.willSaveWaitUntilCalled()) { + this.setWillSaveWaitUntilCalled(true); + completableFuture = CompletableFuture.completedFuture(Collections.singletonList((new TextEdit(this.range0, "WillSaveWaitUntil")))); + } else { + completableFuture = CompletableFuture.completedFuture(Collections.emptyList()); + } + return completableFuture; + } + + public void initialized() { + LanguageServer.super.initialized(); + } + + public CompletableFuture> rangeFormatting(DocumentRangeFormattingParams params) { + Range range = params.getRange(); + int length = range.getEnd().getCharacter() - range.getStart().getCharacter(); + return CompletableFuture.completedFuture(Collections.singletonList(new TextEdit(range, StringUtils.repeat("f", length)))); + } + + public CompletableFuture> codeAction(CodeActionParams params) { + if (params.getRange().getEnd().getCharacter() < 25 && params.getRange().getEnd().getLine() < 1) + return CompletableFuture.completedFuture(Collections.singletonList(new Command(this.commands[0], this.commands[0]))); + else + return CompletableFuture.completedFuture(Arrays.asList(new Command(this.commands[1], this.commands[1]), new Command(this.commands[2], this.commands[2]))); + } + + public CompletableFuture rename(RenameParams params) { + CompletableFuture req = this.client.showMessageRequest(new ShowMessageRequestParams(Arrays.asList( + new MessageActionItem("One"), + new MessageActionItem("Two"), + new MessageActionItem("Three"))) + ); + req.thenAccept(item -> this.client.showMessage(new MessageParams(MessageType.Info, "Chose " + item.getTitle()))); + Map> changes = new HashMap<>(); + changes.put(params.getTextDocument().getUri(), Collections.singletonList(new TextEdit(ranges[0], "Renamed"))); + return CompletableFuture.completedFuture(new WorkspaceEdit(changes)); + } + + public CompletableFuture> formatting(DocumentFormattingParams params) { + String uri = params.getTextDocument().getUri(); + if (uri.contains("test1")) + return CompletableFuture.completedFuture(Collections.singletonList(new TextEdit(this.range0, "Formatted"))); + else + return CompletableFuture.completedFuture(Collections.singletonList(new TextEdit(this.ranges[1], ""))); + } + + public TextDocumentService getTextDocumentService() { + return this; + } + + public void exit() { + System.exit(0); + } + + public CompletableFuture initialize(InitializeParams params) { + return CompletableFuture.completedFuture(new InitializeResult(this.capabilities)); + } + + public void connect(LanguageClient client) { + this.setClient(client); + } + + public WorkspaceService getWorkspaceService() { + return this; + } + + public CompletableFuture shutdown() { + return CompletableFuture.completedFuture(new Object()); + } + + public void initialized(InitializedParams params) { + } + + public TestServer() { + CompletionItem[] completionItems = new CompletionItem[4]; + CompletionItem a = new CompletionItem("A"); + a.setInsertText("CompletionA"); + a.setInsertTextFormat(InsertTextFormat.PlainText); + a.setKind(CompletionItemKind.Enum); + a.setDetail("DetailA"); + a.setDocumentation("DocumentationA"); + completionItems[0] = a; + CompletionItem b = new CompletionItem("B"); + b.setTextEdit(new TextEdit(this.range0, "CompletedB")); + b.setAdditionalTextEdits(Collections.singletonList(new TextEdit(new Range(new Position(0, 20), new Position(0, 30)), "CompletedB"))); + b.setKind(CompletionItemKind.Class); + b.setDetail("DetailB"); + completionItems[1] = b; + CompletionItem c = new CompletionItem("C"); + c.setCommand(new Command(this.commands[0], this.commands[0])); + c.setDocumentation("DocumentationC"); + completionItems[2] = c; + CompletionItem d = new CompletionItem("D"); + d.setKind(CompletionItemKind.Method); + completionItems[3] = d; + + this.completionItems = completionItems; + this.uris = Arrays.asList("temp:///src/test1.test", "temp:///src/test1.test"); + this.capabilities = new ServerCapabilities(); + this.syncKindOptions = new TextDocumentSyncOptions(); + this.positions = Arrays.asList(this.pos0, new Position(0, 10), new Position(0, 20), new Position(0, 30), new Position(1, 0), new Position(1, 10), new Position(1, 20), new Position(1, 30)).toArray(new Position[]{}); + this.capabilities.setCodeActionProvider(true); + this.capabilities.setCodeLensProvider(new CodeLensOptions(true)); + this.capabilities.setCompletionProvider(new CompletionOptions(true, Collections.singletonList("^"))); + this.capabilities.setDefinitionProvider(true); + this.capabilities.setDocumentFormattingProvider(true); + this.capabilities.setDocumentHighlightProvider(true); + this.capabilities.setDocumentLinkProvider(new DocumentLinkOptions(true)); + this.capabilities.setDocumentOnTypeFormattingProvider(new DocumentOnTypeFormattingOptions("\u00b0", Collections.singletonList("\u00a7"))); // "°", "§" + this.capabilities.setDocumentRangeFormattingProvider(true); + this.capabilities.setDocumentSymbolProvider(true); + this.capabilities.setExecuteCommandProvider(new ExecuteCommandOptions(Arrays.asList(this.commands))); + this.capabilities.setHoverProvider(true); + this.capabilities.setReferencesProvider(true); + this.capabilities.setRenameProvider(true); + this.capabilities.setSignatureHelpProvider(new SignatureHelpOptions(Collections.singletonList("("))); + this.ranges = Arrays.stream(this.positions).map(pos -> new Range(pos, pos)).collect(Collectors.toList()).toArray(new Range[]{}); + this.syncKindOptions.setChange(TextDocumentSyncKind.Incremental); + this.syncKindOptions.setOpenClose(true); + this.syncKindOptions.setSave(new SaveOptions(true)); + this.syncKindOptions.setWillSave(true); + this.syncKindOptions.setWillSaveWaitUntil(true); + this.capabilities.setTextDocumentSync(this.syncKindOptions); + this.capabilities.setWorkspaceSymbolProvider(true); + this.setWillSaveWaitUntilCalled(false); + } + + public static void main(String[] args) { + InputStream in = System.in; + PrintStream out = System.out; + TestServer server = new TestServer(); + Launcher launcher = LSPLauncher.createServerLauncher(server, in, out); + LanguageClient client = (LanguageClient) launcher.getRemoteProxy(); + server.connect(client); + launcher.startListening(); + System.err.println("Listening"); + } +} From 13133a17873dfc2a3307f3b9bf2471acacf5dc3f Mon Sep 17 00:00:00 2001 From: Nikita Gryzlov Date: Sat, 17 Nov 2018 22:14:24 +0700 Subject: [PATCH 2/2] First try to add integration completion test --- .../github/gtache/lsp/its/CompletionTest.java | 50 +++++++++++++++++++ .../github/gtache/lsp/utils/TestUtils.java | 10 ++++ 2 files changed, 60 insertions(+) create mode 100644 src/test/java/com/github/gtache/lsp/its/CompletionTest.java create mode 100644 src/test/java/com/github/gtache/lsp/utils/TestUtils.java diff --git a/src/test/java/com/github/gtache/lsp/its/CompletionTest.java b/src/test/java/com/github/gtache/lsp/its/CompletionTest.java new file mode 100644 index 0000000..cb8515a --- /dev/null +++ b/src/test/java/com/github/gtache/lsp/its/CompletionTest.java @@ -0,0 +1,50 @@ +package com.github.gtache.lsp.its; + +import com.github.gtache.lsp.client.languageserver.serverdefinition.ExeLanguageServerDefinition; +import com.github.gtache.lsp.client.languageserver.serverdefinition.LanguageServerDefinition; +import com.github.gtache.lsp.client.languageserver.serverdefinition.UserConfigurableServerDefinition; +import com.github.gtache.lsp.settings.LSPState; +import com.github.gtache.lsp.utils.TestUtils; +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CompletionTest extends LightCodeInsightFixtureTestCase { + @Override + protected void setUp() throws Exception { + super.setUp(); + Path languageServer = Paths.get(".", "testServer", "build", "libs", "testServer.jar"); + + List args = new ArrayList<>(); + args.add("-jar"); + args.add(languageServer.toString()); + args.add("-Xdebug"); + args.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1044"); + ExeLanguageServerDefinition definition = new ExeLanguageServerDefinition("test", "java", args.toArray(new String[0])); + + LSPState state = LSPState.getInstance(); + Map servers = new HashMap<>(); + servers.put("test", definition); + state.setExtToServ(servers); + } + + @Override + protected String getTestDataPath() { + return TestUtils.BASE_TEST_DATA_PATH; + } + + public void testCompletion() { + myFixture.configureByFiles("test1.test", "test2.test"); + myFixture.complete(CompletionType.BASIC, 1); + List strings = myFixture.getLookupElementStrings(); + assertTrue(strings.containsAll(Arrays.asList("key with spaces", "language", "message", "tab", "website"))); + assertEquals(5, strings.size()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/gtache/lsp/utils/TestUtils.java b/src/test/java/com/github/gtache/lsp/utils/TestUtils.java new file mode 100644 index 0000000..fa6d346 --- /dev/null +++ b/src/test/java/com/github/gtache/lsp/utils/TestUtils.java @@ -0,0 +1,10 @@ +package com.github.gtache.lsp.utils; + +import java.io.File; + +public class TestUtils { + /** + * The root of the test data directory + */ + public static final String BASE_TEST_DATA_PATH = new File("src/test/resources").getAbsolutePath(); +} \ No newline at end of file