[lsi] extracted factory methods and json based language server launcher

to lsapi.services project

Change-Id: Ifb266b8db0567b25eeafa8d76ba503bea0439fdb
Signed-off-by: akosyakov <anton.kosyakov@typefox.io>
This commit is contained in:
akosyakov 2016-05-31 09:29:56 +02:00
parent c519572159
commit bb0df97e60
11 changed files with 56 additions and 247 deletions

View file

@ -50,7 +50,7 @@ class RequestManager {
/**
* <p>
* The given <i>write request</i> will be ran first when <i>all running requests</i> completed.
* The given <i>write request</i> will be run first when <i>all running requests</i> completed.
* </p>
* <p>
* Currently <i>running requests</i> will be cancelled.
@ -85,7 +85,7 @@ class RequestManager {
/**
* <p>
* The given <i>read request</i> will be ran:
* The given <i>read request</i> will be run:
* <ul>
* <li>concurrent with <i>running read requests</i>;</li>
* <li>first when <i>running write requests</i> completed.</li>

View file

@ -11,16 +11,12 @@ import com.google.inject.AbstractModule
import com.google.inject.Guice
import com.google.inject.Inject
import io.typefox.lsapi.Diagnostic
import io.typefox.lsapi.DidOpenTextDocumentParamsImpl
import io.typefox.lsapi.InitializeParamsImpl
import io.typefox.lsapi.InitializeResult
import io.typefox.lsapi.Location
import io.typefox.lsapi.Position
import io.typefox.lsapi.PublishDiagnosticsParams
import io.typefox.lsapi.Range
import io.typefox.lsapi.TextDocumentIdentifierImpl
import io.typefox.lsapi.TextDocumentItemImpl
import io.typefox.lsapi.TextDocumentPositionParamsImpl
import java.io.File
import java.io.FileWriter
import java.net.URI
@ -29,6 +25,7 @@ import java.nio.file.Paths
import java.util.List
import java.util.Map
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import org.eclipse.xtext.ide.server.LanguageServerImpl
import org.eclipse.xtext.ide.server.ServerModule
import org.eclipse.xtext.ide.server.UriExtensions
@ -39,7 +36,6 @@ import org.eclipse.xtext.util.Modules2
import org.junit.Before
import static io.typefox.lsapi.util.LsapiFactories.*
import java.util.function.Consumer
/**
* @author Sven Efftinge - Initial contribution and API
@ -100,20 +96,17 @@ class AbstractLanguageServerTest implements Consumer<PublishDiagnosticsParams> {
}
protected def InitializeResult initialize((InitializeParamsImpl)=>void initializer) {
val params = new InitializeParamsImpl
params.rootPath = rootPath.toString
val params = newInitializeParams(1, rootPath.toString)
initializer?.apply(params)
return languageServer.initialize(params).get
}
protected def void open(String fileUri, String model) {
languageServer.didOpen(new DidOpenTextDocumentParamsImpl => [
textDocument = new TextDocumentItemImpl => [
uri = fileUri
version = 1
text = model
]
])
languageServer.didOpen(newDidOpenTextDocumentParams(fileUri, "testlang", 1, model))
}
protected def void close(String fileUri) {
languageServer.didClose(newDidCloseTextDocumentParams(fileUri))
}
def String ->(String path, CharSequence contents) {
@ -132,21 +125,6 @@ class AbstractLanguageServerTest implements Consumer<PublishDiagnosticsParams> {
diagnostics.put(t.uri, t.diagnostics)
}
// FIXME: move to LsapiFactories
protected def TextDocumentPositionParamsImpl newPosition(String uri, int line, int column) {
val params = new TextDocumentPositionParamsImpl
params.textDocument = uri.newIdentifier
params.position = newPosition(line, column)
return params
}
// FIXME: move to LsapiFactories
protected def TextDocumentIdentifierImpl newIdentifier(String uri) {
val identifier = new TextDocumentIdentifierImpl
identifier.uri = uri
return identifier
}
protected def dispatch String toExpectation(List<?> elements) '''
«FOR element : elements»
«element.toExpectation»

View file

@ -11,6 +11,7 @@ import io.typefox.lsapi.CompletionItem
import org.eclipse.xtend.lib.annotations.Accessors
import org.junit.Test
import static io.typefox.lsapi.util.LsapiFactories.*
import static org.junit.Assert.*
/**
@ -67,7 +68,7 @@ class CompletionTest extends AbstractLanguageServerTest {
initialize
open(fileUri, model)
val completionItems = languageServer.completion(newPosition(fileUri, line, column))
val completionItems = languageServer.completion(newTextDocumentPositionParams(fileUri, line, column))
val actualCompletionItems = completionItems.get.items.toExpectation
assertEquals(expectedCompletionItems, actualCompletionItems)

View file

@ -11,6 +11,7 @@ import org.eclipse.xtend.lib.annotations.Accessors
import org.junit.Test
import static org.junit.Assert.*
import static io.typefox.lsapi.util.LsapiFactories.*
/**
* @author kosyakov - Initial contribution and API
@ -43,7 +44,7 @@ class DefinitionTest extends AbstractLanguageServerTest {
initialize
open(fileUri, model)
val definitions = languageServer.definition(newPosition(fileUri, line, column))
val definitions = languageServer.definition(newTextDocumentPositionParams(fileUri, line, column))
val actualDefinitions = definitions.get.toExpectation
assertEquals(expectedDefinitions, actualDefinitions)
}

View file

@ -7,13 +7,14 @@
*******************************************************************************/
package org.eclipse.xtext.ide.tests.server
import io.typefox.lsapi.DocumentSymbolParamsImpl
import io.typefox.lsapi.SymbolInformation
import org.eclipse.xtend.lib.annotations.Accessors
import org.junit.Test
import static org.junit.Assert.*
import static extension io.typefox.lsapi.util.LsapiFactories.*
/**
* @author kosyakov - Initial contribution and API
*/
@ -66,9 +67,7 @@ class DocumentSymbolTest extends AbstractLanguageServerTest {
initialize
open(fileUri, model)
val symbols = languageServer.documentSymbol(new DocumentSymbolParamsImpl => [
textDocument = fileUri.newIdentifier
])
val symbols = languageServer.documentSymbol(fileUri.newDocumentSymbolParams)
val String actualSymbols = symbols.get.toExpectation
assertEquals(expectedSymbols, actualSymbols)
}

View file

@ -7,22 +7,13 @@
*******************************************************************************/
package org.eclipse.xtext.ide.tests.server
import io.typefox.lsapi.DidChangeTextDocumentParamsImpl
import io.typefox.lsapi.DidChangeWatchedFilesParamsImpl
import io.typefox.lsapi.DidCloseTextDocumentParamsImpl
import io.typefox.lsapi.DidOpenTextDocumentParamsImpl
import io.typefox.lsapi.FileEvent
import io.typefox.lsapi.FileEventImpl
import io.typefox.lsapi.PositionImpl
import io.typefox.lsapi.RangeImpl
import io.typefox.lsapi.TextDocumentContentChangeEventImpl
import io.typefox.lsapi.TextDocumentIdentifierImpl
import io.typefox.lsapi.TextDocumentItemImpl
import io.typefox.lsapi.VersionedTextDocumentIdentifierImpl
import org.junit.Test
import static org.junit.Assert.*
import static extension io.typefox.lsapi.util.LsapiFactories.*
/**
* @author Sven Efftinge - Initial contribution and API
*/
@ -43,35 +34,24 @@ class OpenDocumentTest extends AbstractLanguageServerTest {
type Foo {
}
'''
languageServer.getWorkspaceService.didChangeWatchedFiles(new DidChangeWatchedFilesParamsImpl => [
changes = newArrayList(new FileEventImpl => [
uri = path
type = FileEvent.TYPE_CREATED
])
])
languageServer.getWorkspaceService.didChangeWatchedFiles(
#[
newFileEvent(path, FileEvent.TYPE_CREATED)
].newDidChangeWatchedFilesParams
)
// still errorneous
assertEquals("Couldn't resolve reference to TypeDeclaration 'NonExisting'.", diagnostics.get(firstFile).head.message)
// let's open the document with a different content
languageServer.didOpen(new DidOpenTextDocumentParamsImpl => [
textDocument = new TextDocumentItemImpl => [
uri = path
version = 1
text = '''
type NonExisting {
}
'''
]
])
open(path, '''
type NonExisting {
}
''')
assertNull(diagnostics.get(firstFile).head)
// let's close again
languageServer.didClose(new DidCloseTextDocumentParamsImpl => [
textDocument = new TextDocumentIdentifierImpl => [
uri = path
]
])
close(path)
assertEquals("Couldn't resolve reference to TypeDeclaration 'NonExisting'.", diagnostics.get(firstFile).head.message)
}
@ -87,40 +67,16 @@ class OpenDocumentTest extends AbstractLanguageServerTest {
assertEquals("Couldn't resolve reference to TypeDeclaration 'NonExisting'.", diagnostics.get(firstFile).head.message)
languageServer.didOpen(new DidOpenTextDocumentParamsImpl => [
textDocument = new TextDocumentItemImpl => [
uri = firstFile
version = 1
text = '''
type Test {
NonExisting foo
}
'''
]
])
open(firstFile, '''
type Test {
NonExisting foo
}
''')
assertEquals("Couldn't resolve reference to TypeDeclaration 'NonExisting'.", diagnostics.get(firstFile).head.message)
languageServer.didChange(new DidChangeTextDocumentParamsImpl => [
textDocument = new VersionedTextDocumentIdentifierImpl => [
uri = firstFile
version = 2
]
contentChanges = #[
new TextDocumentContentChangeEventImpl => [
range = new RangeImpl => [
start = new PositionImpl => [
line = 1
character = 4
]
end = new PositionImpl => [
line = 1
character = 15
]
]
text = "Test"
]
]
])
languageServer.didChange(newDidChangeTextDocumentParamsImpl(firstFile, 2, #[
newTextDocumentContentChangeEvent(newRange(newPosition(1, 4), newPosition(1, 15)), null, "Test")
]))
assertNull(diagnostics.get(firstFile).head)
}
}

View file

@ -8,7 +8,6 @@
package org.eclipse.xtext.ide.tests.server
import io.typefox.lsapi.ReferenceContextImpl
import io.typefox.lsapi.ReferenceParamsImpl
import org.eclipse.xtend.lib.annotations.Accessors
import org.junit.Test
@ -53,7 +52,6 @@ class ReferenceTest extends AbstractLanguageServerTest {
'''
]
}
protected def void testReferences((ReferenceTestConfiguration)=>void configurator) {
val extension configuration = new ReferenceTestConfiguration
@ -66,11 +64,7 @@ class ReferenceTest extends AbstractLanguageServerTest {
val referenceContext = new ReferenceContextImpl
referenceContext.includeDeclaration = includeDeclaration
val definitions = languageServer.references(new ReferenceParamsImpl => [
textDocument = fileUri.newIdentifier
position = newPosition(line, column)
context = referenceContext
])
val definitions = languageServer.references(newReferenceParams(fileUri, line, column, referenceContext))
val actualDefinitions = definitions.get.toExpectation
assertEquals(expectedReferences, actualDefinitions)
}

View file

@ -7,13 +7,13 @@
*******************************************************************************/
package org.eclipse.xtext.ide.tests.server
import io.typefox.lsapi.DidChangeWatchedFilesParamsImpl
import io.typefox.lsapi.FileEvent
import io.typefox.lsapi.FileEventImpl
import org.junit.Test
import static org.junit.Assert.*
import static extension io.typefox.lsapi.util.LsapiFactories.*
/**
* @author Sven Efftinge - Initial contribution and API
*/
@ -59,12 +59,10 @@ class ServerTest extends AbstractLanguageServerTest {
type NonExisting {
}
'''
languageServer.getWorkspaceService.didChangeWatchedFiles(new DidChangeWatchedFilesParamsImpl => [
changes = newArrayList(new FileEventImpl => [
uri = path
type = FileEvent.TYPE_CREATED
])
])
languageServer.getWorkspaceService.didChangeWatchedFiles(
#[newFileEvent(path, FileEvent.TYPE_CREATED)].newDidChangeWatchedFilesParams
)
assertNotNull(diagnostics.get(path))
assertTrue(diagnostics.values.join(','), diagnostics.values.forall[empty])
}

View file

@ -55,10 +55,9 @@ class RequestManagerTest {
}
sharedState.incrementAndGet
]
val future2 = requestManager.runRead [
requestManager.runRead [
sharedState.incrementAndGet
]
future2.join
future.join
assertEquals(2, sharedState.get)
}
@ -87,15 +86,13 @@ class RequestManagerTest {
@Test
def void testRunWriteAfterWrite() {
val future = requestManager.runWrite [
requestManager.runWrite [
sharedState.incrementAndGet
]
val future2 = requestManager.runWrite [
requestManager.runWrite [
if (sharedState.get != 0)
sharedState.incrementAndGet
]
future2.join
future.join
].join
assertEquals(2, sharedState.get)
}

View file

@ -8,13 +8,9 @@
package org.eclipse.xtext.ide.tests.testlanguage.server
import com.google.inject.Guice
import java.io.IOException
import java.io.PrintWriter
import io.typefox.lsapi.services.LanguageServer
import io.typefox.lsapi.services.json.LanguageServerLauncher
import java.net.InetSocketAddress
import java.nio.channels.AsynchronousServerSocketChannel
import java.nio.channels.AsynchronousSocketChannel
import java.nio.channels.Channels
import java.nio.channels.CompletionHandler
import org.eclipse.xtext.ide.server.ServerModule
/**
@ -23,51 +19,13 @@ import org.eclipse.xtext.ide.server.ServerModule
class SocketServerLauncher {
def static void main(String[] args) {
var AsynchronousServerSocketChannel serverSocket
try {
val injector = Guice.createInjector(new ServerModule)
val server = injector.getInstance(VSCodeJsonAdapter)
server.errorLog = new PrintWriter(System.err)
server.messageLog = new PrintWriter(System.out)
serverSocket = AsynchronousServerSocketChannel.open
val address = new InetSocketAddress('localhost', 5007)
serverSocket.bind(address)
println('Listening to ' + address)
serverSocket.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
override completed(AsynchronousSocketChannel channel, Object attachment) {
val in = Channels.newInputStream(channel)
val out = Channels.newOutputStream(channel)
println('Connection accepted')
server.connect(in, out)
server.start()
server.join()
channel.close()
println('Connection closed')
}
override failed(Throwable exc, Object attachment) {
exc.printStackTrace
}
})
while (!server.exitRequested) {
Thread.sleep(2000)
}
} catch (Throwable t) {
t.printStackTrace()
} finally {
if (serverSocket !== null) {
try {
serverSocket.close()
} catch (IOException e) {
}
}
}
val injector = Guice.createInjector(new ServerModule)
val languageServer = injector.getInstance(LanguageServer)
val launcher = LanguageServerLauncher.newLoggingLauncher(
languageServer,
new InetSocketAddress('localhost', 5007)
)
launcher.launch
}
}

View file

@ -1,73 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 TypeFox GmbH (http://www.typefox.io) and others.
* 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
*******************************************************************************/
package org.eclipse.xtext.ide.tests.testlanguage.server
import com.google.inject.Inject
import io.typefox.lsapi.NotificationMessage
import io.typefox.lsapi.RequestMessage
import io.typefox.lsapi.ResponseMessage
import io.typefox.lsapi.services.LanguageServer
import io.typefox.lsapi.services.json.LanguageServerToJsonAdapter
import io.typefox.lsapi.services.json.MessageMethods
import java.io.PrintWriter
import org.eclipse.xtend.lib.annotations.Accessors
class VSCodeJsonAdapter extends LanguageServerToJsonAdapter {
@Accessors(PUBLIC_SETTER)
PrintWriter errorLog
@Accessors(PUBLIC_SETTER)
PrintWriter messageLog
@Accessors(PUBLIC_GETTER)
boolean exitRequested
@Inject
new(LanguageServer server) {
super(server)
protocol.addErrorListener[ message, throwable |
if (errorLog !== null) {
if (throwable !== null)
throwable.printStackTrace(errorLog)
else if (message !== null)
errorLog.println(message)
errorLog.flush()
}
]
protocol.addIncomingMessageListener[ message, json |
if (message instanceof RequestMessage) {
switch message.method {
case MessageMethods.EXIT:
exitRequested = true
}
}
if (messageLog !== null) {
switch message {
RequestMessage:
messageLog.println('Client Request:\n\t' + json)
NotificationMessage:
messageLog.println('Client Notification:\n\t' + json)
}
messageLog.flush()
}
]
protocol.addOutgoingMessageListener[ message, json |
if (messageLog !== null) {
switch message {
ResponseMessage:
messageLog.println('Server Response:\n\t' + json)
NotificationMessage:
messageLog.println('Server Notification:\n\t' + json)
}
messageLog.flush()
}
]
}
}