blob: bd2caa8ec9f2c7e8aac589afd6f5294ca6e6dda7 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2016-2019 TypeFox and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
******************************************************************************/
package org.eclipse.lsp4j.test.services
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.internal.LazilyParsedNumber
import java.util.ArrayList
import java.util.Collection
import java.util.HashMap
import java.util.List
import org.eclipse.lsp4j.ClientCapabilities
import org.eclipse.lsp4j.CodeAction
import org.eclipse.lsp4j.CodeActionCapabilities
import org.eclipse.lsp4j.CodeLens
import org.eclipse.lsp4j.CodeLensCapabilities
import org.eclipse.lsp4j.ColorProviderCapabilities
import org.eclipse.lsp4j.Command
import org.eclipse.lsp4j.CompletionCapabilities
import org.eclipse.lsp4j.CompletionItemCapabilities
import org.eclipse.lsp4j.CompletionItemKind
import org.eclipse.lsp4j.CompletionItemKindCapabilities
import org.eclipse.lsp4j.CompletionParams
import org.eclipse.lsp4j.CreateFile
import org.eclipse.lsp4j.CreateFileOptions
import org.eclipse.lsp4j.DefinitionCapabilities
import org.eclipse.lsp4j.DeleteFile
import org.eclipse.lsp4j.Diagnostic
import org.eclipse.lsp4j.DiagnosticSeverity
import org.eclipse.lsp4j.DidChangeTextDocumentParams
import org.eclipse.lsp4j.DocumentFormattingParams
import org.eclipse.lsp4j.DocumentHighlightCapabilities
import org.eclipse.lsp4j.DocumentLinkCapabilities
import org.eclipse.lsp4j.DocumentSymbol
import org.eclipse.lsp4j.DocumentSymbolCapabilities
import org.eclipse.lsp4j.FormattingCapabilities
import org.eclipse.lsp4j.FormattingOptions
import org.eclipse.lsp4j.Hover
import org.eclipse.lsp4j.HoverCapabilities
import org.eclipse.lsp4j.ImplementationCapabilities
import org.eclipse.lsp4j.InitializeParams
import org.eclipse.lsp4j.Location
import org.eclipse.lsp4j.LocationLink
import org.eclipse.lsp4j.MarkedString
import org.eclipse.lsp4j.MarkupContent
import org.eclipse.lsp4j.MarkupKind
import org.eclipse.lsp4j.OnTypeFormattingCapabilities
import org.eclipse.lsp4j.ParameterInformation
import org.eclipse.lsp4j.Position
import org.eclipse.lsp4j.PrepareRenameResult
import org.eclipse.lsp4j.ProgressParams
import org.eclipse.lsp4j.PublishDiagnosticsParams
import org.eclipse.lsp4j.Range
import org.eclipse.lsp4j.RangeFormattingCapabilities
import org.eclipse.lsp4j.ReferencesCapabilities
import org.eclipse.lsp4j.RenameCapabilities
import org.eclipse.lsp4j.RenameFile
import org.eclipse.lsp4j.ResourceOperation
import org.eclipse.lsp4j.SignatureHelp
import org.eclipse.lsp4j.SignatureHelpCapabilities
import org.eclipse.lsp4j.SignatureInformation
import org.eclipse.lsp4j.SignatureInformationCapabilities
import org.eclipse.lsp4j.SymbolInformation
import org.eclipse.lsp4j.SymbolKind
import org.eclipse.lsp4j.SymbolKindCapabilities
import org.eclipse.lsp4j.SynchronizationCapabilities
import org.eclipse.lsp4j.TextDocumentClientCapabilities
import org.eclipse.lsp4j.TextDocumentContentChangeEvent
import org.eclipse.lsp4j.TextDocumentEdit
import org.eclipse.lsp4j.TextDocumentIdentifier
import org.eclipse.lsp4j.TextEdit
import org.eclipse.lsp4j.TypeDefinitionCapabilities
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier
import org.eclipse.lsp4j.WorkDoneProgressCancelParams
import org.eclipse.lsp4j.WorkDoneProgressCreateParams
import org.eclipse.lsp4j.WorkDoneProgressEnd
import org.eclipse.lsp4j.WorkDoneProgressNotification
import org.eclipse.lsp4j.WorkspaceClientCapabilities
import org.eclipse.lsp4j.WorkspaceEdit
import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler
import org.eclipse.lsp4j.jsonrpc.messages.Either
import org.eclipse.lsp4j.jsonrpc.messages.Message
import org.eclipse.lsp4j.jsonrpc.messages.MessageIssue
import org.eclipse.lsp4j.jsonrpc.messages.NotificationMessage
import org.eclipse.lsp4j.jsonrpc.messages.RequestMessage
import org.eclipse.lsp4j.jsonrpc.messages.ResponseError
import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import org.eclipse.lsp4j.jsonrpc.messages.Tuple
import org.eclipse.lsp4j.jsonrpc.services.ServiceEndpoints
import org.eclipse.lsp4j.services.LanguageClient
import org.eclipse.lsp4j.services.LanguageServer
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
import org.junit.Before
import org.junit.Test
import static org.junit.Assert.*
class JsonParseTest {
/**
* Gson parses numbers with {@link LazilyParsedNumber}, which is not
* equals-compatible with {@link Integer}.
*/
@FinalFieldsConstructor
private static class MyInteger extends Number {
val int value
override doubleValue() { value }
override floatValue() { value }
override intValue() { value }
override longValue() { value }
override equals(Object obj) {
if (obj instanceof Number) {
return value as double == obj.doubleValue
}
return false
}
override hashCode() {
Integer.hashCode(value)
}
override toString() {
Integer.toString(value)
}
}
MessageJsonHandler jsonHandler
@Before
def void setup() {
val methods = ServiceEndpoints.getSupportedMethods(LanguageServer)
val clientMethods = ServiceEndpoints.getSupportedMethods(LanguageClient)
val all = new HashMap
all.putAll(methods)
all.putAll(clientMethods)
jsonHandler = new MessageJsonHandler(all)
}
private def void assertParse(CharSequence json, Message expected) {
val actual = jsonHandler.parseMessage(json)
assertEquals(expected.toString, actual.toString)
assertEquals(expected, actual)
}
@Test
def void testCompletion() {
'''
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/completion",
"params": {
"textDocument": {
"uri": "file:///tmp/foo"
},
"position": {
"line": 4,
"character": 22
}
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = 1
method = MessageMethods.DOC_COMPLETION
params = new CompletionParams => [
textDocument = new TextDocumentIdentifier => [
uri = "file:///tmp/foo"
]
position = new Position => [
line = 4
character = 22
]
]
])
}
@Test
def void testDidChange() {
'''
{
"jsonrpc": "2.0",
"method": "textDocument/didChange",
"params": {
"textDocument": {
"uri": "file:///tmp/foo",
"version": 1234
},
"contentChanges": [
{
"range": {
"start": {
"line": 7,
"character": 12
},
"end": {
"line": 8,
"character": 16
}
},
"rangeLength": 20,
"text": "bar"
}
]
}
}
'''.assertParse(new NotificationMessage => [
jsonrpc = "2.0"
method = MessageMethods.DID_CHANGE_DOC
params = new DidChangeTextDocumentParams => [
textDocument = new VersionedTextDocumentIdentifier => [
uri = "file:///tmp/foo"
version = 1234
]
contentChanges = new ArrayList => [
add(new TextDocumentContentChangeEvent => [
range = new Range => [
start = new Position(7, 12)
end = new Position(8, 16)
]
rangeLength = 20
text = "bar"
])
]
]
])
}
@Test
def void testPublishDiagnostics() {
'''
{
"jsonrpc": "2.0",
"method": "textDocument/publishDiagnostics",
"params": {
"uri": "file:///tmp/foo",
"diagnostics": [
{
"message": "Couldn\u0027t resolve reference to State \u0027bar\u0027.",
"range": {
"start": {
"character": 22,
"line": 4
},
"end": {
"character": 25,
"line": 4
}
},
"severity": 1
}
],
version: 1
}
}
'''.assertParse(new NotificationMessage => [
jsonrpc = "2.0"
method = MessageMethods.SHOW_DIAGNOSTICS
params = new PublishDiagnosticsParams => [
uri = "file:///tmp/foo"
diagnostics = new ArrayList => [
add(new Diagnostic => [
range = new Range => [
start = new Position(4, 22)
end = new Position(4, 25)
]
severity = DiagnosticSeverity.Error
message = "Couldn't resolve reference to State 'bar'."
])
]
version = 1
]
])
}
@Test
def void testDocumentSymbolResponse1() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_SYMBOL
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"name" : "foobar",
"kind" : 9,
"location" : {
"uri": "file:/baz.txt",
"range" : {
"start": {
"character": 22,
"line": 4
},
"end": {
"character": 25,
"line": 4
}
}
}
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = newArrayList(Either.forLeft(
new SymbolInformation => [
name = "foobar"
kind = SymbolKind.Constructor
location = new Location => [
uri = "file:/baz.txt"
range = new Range => [
start = new Position(4, 22)
end = new Position(4, 25)
]
]
]
))
])
}
@Test
def void testDocumentSymbolResponse2() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_SYMBOL
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"name" : "foobar",
"kind" : 9,
"range" : {
"start": {
"character": 22,
"line": 4
},
"end": {
"character": 25,
"line": 4
}
},
"selectionRange": {
"start": {
"character": 22,
"line": 4
},
"end": {
"character": 25,
"line": 4
}
}
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = newArrayList(Either.forRight(
new DocumentSymbol => [
name = "foobar"
kind = SymbolKind.Constructor
range = new Range => [
start = new Position(4, 22)
end = new Position(4, 25)
]
selectionRange = new Range => [
start = new Position(4, 22)
end = new Position(4, 25)
]
]
))
])
}
@Test
def void testCodeActionResponse1() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_CODE_ACTION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"title": "fixme",
"command": "fix"
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = newArrayList(Either.forLeft(
new Command => [
title = "fixme"
command = "fix"
]
))
])
}
@Test
def void testCodeActionResponse2() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_CODE_ACTION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"title": "fixme",
"kind": "fix",
"diagnostics": [],
"edit": {
"changes": {
"file:test1533196529126.lspt": [
{
"range": {
"start": {
"line": 0,
"character": 0
},
"end": {
"line": 0,
"character": 5
}
},
"newText": "fixed"
}
]
}
}
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = newArrayList(Either.forRight(
new CodeAction => [
title = "fixme"
kind = "fix"
diagnostics = newArrayList
edit = new WorkspaceEdit => [
changes.put("file:test1533196529126.lspt", newArrayList(
new TextEdit => [
range = new Range => [
start = new Position(0, 0)
end = new Position(0, 5)
]
newText = "fixed"
]
))
]
]
))
])
}
@Test
def void testCodeActionResponse3() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_CODE_ACTION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"title": "fixme"
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = newArrayList(Either.forRight(
new CodeAction => [
title = "fixme"
]
))
])
}
@Test
def void testPrepareRenameResponse1() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_PREPARE_RENAME
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"range": {
"start": {
"character": 32,
"line": 3
},
"end": {
"character": 35,
"line": 3
}
},
"placeholder": "someVar"
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.forRight(new PrepareRenameResult => [
range = new Range => [
start = new Position(3, 32)
end = new Position(3, 35)
]
placeholder = "someVar"
])
])
}
@Test
def void testPrepareRenameResponse2() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_PREPARE_RENAME
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"start": {
"character": 32,
"line": 3
},
"end": {
"character": 35,
"line": 3
}
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.forLeft(new Range => [
start = new Position(3, 32)
end = new Position(3, 35)
])
])
}
@Test
def void testRenameResponse1() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_RENAME
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"changes": {
"file:///tmp/foo": [
{
"range": {
"start": {
"character": 32,
"line": 3
},
"end": {
"character": 35,
"line": 3
}
},
"newText": "foobar"
},
{
"range": {
"start": {
"character": 22,
"line": 4
},
"end": {
"character": 25,
"line": 4
}
},
"newText": "foobar"
}
]
}
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = new WorkspaceEdit => [
changes = new HashMap => [
put("file:///tmp/foo", newArrayList(
new TextEdit => [
range = new Range => [
start = new Position(3, 32)
end = new Position(3, 35)
]
newText = "foobar"
],
new TextEdit => [
range = new Range => [
start = new Position(4, 22)
end = new Position(4, 25)
]
newText = "foobar"
]
))
]
]
])
}
@Test
def void testRenameResponse3() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_RENAME
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"documentChanges": [
{
"kind": "create",
"uri": "file:/foo.txt",
"options": {
"overwrite": true,
"ignoreIfExists": true
}
},
{
"kind": "delete",
"uri": "file:/foo.txt"
},
{
"kind": "rename",
"oldUri": "file:/foo.txt",
"newUri": "file:/bar.txt"
},
{
"textDocument": {
"uri": "file:/baz.txt",
"version": 17
},
"edits": [
{
"range": {
"start": {
"character": 32,
"line": 3
},
"end": {
"character": 35,
"line": 3
}
},
"newText": "asdfqweryxcv"
}
]
}
]
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = new WorkspaceEdit => [
documentChanges = newArrayList(
Either.forRight((new CreateFile => [
uri = "file:/foo.txt"
options = new CreateFileOptions => [
overwrite = true
ignoreIfExists = true
]
]) as ResourceOperation),
Either.forRight((new DeleteFile => [
uri = "file:/foo.txt"
]) as ResourceOperation),
Either.forRight((new RenameFile => [
oldUri = "file:/foo.txt"
newUri = "file:/bar.txt"
]) as ResourceOperation),
Either.forLeft(new TextDocumentEdit => [
textDocument = new VersionedTextDocumentIdentifier("file:/baz.txt", 17)
edits = newArrayList(
new TextEdit => [
range = new Range => [
start = new Position(3, 32)
end = new Position(3, 35)
]
newText = "asdfqweryxcv"
]
)
])
)
]
])
}
@Test
def void testResponseError() {
jsonHandler.methodProvider = [ id |
switch id {
case '12':
'textDocument/rename'
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"error": {
"code": -32600,
"message": "Could not parse request."
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
error = new ResponseError => [
code = ResponseErrorCode.InvalidRequest
message = "Could not parse request."
]
])
}
@Test
def void testTelemetry() {
'''
{
"jsonrpc": "2.0",
"method": "telemetry/event",
"params": {
"foo": 12.3,
"bar": "qwertz"
}
}
'''.assertParse(new NotificationMessage => [
jsonrpc = "2.0"
method = MessageMethods.TELEMETRY_EVENT
params = newLinkedHashMap('foo' -> 12.3, 'bar' -> 'qwertz')
])
}
@Test
def void testHoverResponse1() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_HOVER
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"range": {
"start": {
"character": 32,
"line": 3
},
"end": {
"character": 35,
"line": 3
}
},
"contents": [
"foo",
"boo shuby doo"
]
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = new Hover => [
range = new Range => [
start = new Position(3, 32)
end = new Position(3, 35)
]
contents = newArrayList(
Either.forLeft("foo"),
Either.forLeft("boo shuby doo")
)
]
])
}
@Test
def void testHoverResponse2() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_HOVER
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"contents": {
"kind": "plaintext",
"value": "foo"
}
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = new Hover => [
contents = new MarkupContent => [
kind = "plaintext"
value = "foo"
]
]
])
}
@Test
def void testHoverResponse3() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_HOVER
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"contents": "foo"
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = new Hover => [
contents = newArrayList(Either.forLeft("foo"))
]
])
}
@Test
def void testHoverResponse4() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_HOVER
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"contents": {
"language": "plaintext",
"value": "foo"
}
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = new Hover => [
contents = newArrayList(Either.forRight(new MarkedString => [
language = "plaintext"
value = "foo"
]))
]
])
}
@Test
def void testCodeLensResponse() {
val json = '''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"range": {
"start": {
"character": 32,
"line": 3
},
"end": {
"character": 35,
"line": 3
}
},
"command": {
"title": "save",
"command": "saveCommand",
"arguments": [
{
"uri": "file:/foo",
"version": 5
}
]
},
"data": [
42,
"qwert",
{
"key": "value"
}
]
}
}
'''
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_CODE_LENS
}
]
val message = jsonHandler.parseMessage(json)
assertTrue("Expected a ResponseMessage", message instanceof ResponseMessage)
val response = message as ResponseMessage
assertTrue("Expected a Collection in result", response.result instanceof Collection<?>)
val result = response.result as Collection<?>
assertTrue("Expected a CodeLens in result[0]", result.head instanceof CodeLens)
val codeLens = result.head as CodeLens
assertNotNull(codeLens.command)
val argument = codeLens.command.arguments.head
assertTrue("Expected a JsonObject in command.arguments[0]", argument instanceof JsonObject)
assertEquals("file:/foo", (argument as JsonObject).get("uri").asString)
assertEquals(5, (argument as JsonObject).get("version").asInt)
assertTrue("Expected a JsonArray in data", codeLens.data instanceof JsonArray)
val data = codeLens.data as JsonArray
assertEquals(42, data.get(0).asInt)
assertEquals("qwert", data.get(1).asString)
assertTrue("Expected a JsonObject in data[2]", data.get(2) instanceof JsonObject)
assertEquals("value", (data.get(2) as JsonObject).get("key").asString)
}
@Test
def void testDeclarationResponse() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_DECLARATION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"uri": "foo",
"range": {
"start": {
"line": 7,
"character": 12
},
"end": {
"line": 8,
"character": 16
}
}
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.<List<? extends Location>, List<? extends LocationLink>>forLeft(#[
new Location('foo', new Range(new Position(7, 12), new Position(8, 16)))
])
])
}
@Test
def void testItemInsteadOfListResponse() {
//test parse direct item without the list
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_DECLARATION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"uri": "foo",
"range": {
"start": {
"line": 7,
"character": 12
},
"end": {
"line": 8,
"character": 16
}
}
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.<List<? extends Location>, List<? extends LocationLink>>forLeft(#[
new Location('foo', new Range(new Position(7, 12), new Position(8, 16)))
])
])
}
@Test
def void testDefinitionResponse1() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_DEFINITION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"targetUri": "foo",
"targetRange": {
"start": {
"line": 7,
"character": 12
},
"end": {
"line": 8,
"character": 16
}
}
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.<List<? extends Location>, List<? extends LocationLink>>forRight(#[
new LocationLink => [
targetUri = 'foo'
targetRange = new Range(new Position(7, 12), new Position(8, 16))
]
])
])
}
@Test
def void testDefinitionResponse2() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_DEFINITION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": []
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.<List<? extends Location>, List<? extends LocationLink>>forLeft(emptyList)
])
}
@Test
def void testTypeDefinitionResponse() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_TYPE_DEFINITION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"uri": "foo",
"range": {
"start": {
"line": 7,
"character": 12
},
"end": {
"line": 8,
"character": 16
}
}
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.<List<? extends Location>, List<? extends LocationLink>>forLeft(#[
new Location('foo', new Range(new Position(7, 12), new Position(8, 16)))
])
])
}
@Test
def void testImplementationResponse() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_IMPLEMENTATION
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": [
{
"targetUri": "foo",
"targetRange": {
"start": {
"line": 7,
"character": 12
},
"end": {
"line": 8,
"character": 16
}
}
}
]
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = Either.<List<? extends Location>, List<? extends LocationLink>>forRight(#[
new LocationLink => [
targetUri = 'foo'
targetRange = new Range(new Position(7, 12), new Position(8, 16))
]
])
])
}
@Test
def void testSignatureHelpResponse() {
jsonHandler.methodProvider = [ id |
switch id {
case '12': MessageMethods.DOC_SIGNATURE_HELP
}
]
'''
{
"jsonrpc": "2.0",
"id": "12",
"result": {
"signatures": [
{
"label": "Foo",
"parameters": [
{
"label": "label1"
},
{
"label": [12, 25]
}
]
}
]
}
}
'''.assertParse(new ResponseMessage => [
jsonrpc = "2.0"
id = "12"
result = new SignatureHelp => [
signatures = #[
new SignatureInformation => [
label = "Foo"
parameters = #[
new ParameterInformation => [
label = Either.forLeft("label1")
],
new ParameterInformation => [
label = Either.forRight(Tuple.two(12, 25))
]
]
]
]
]
])
}
@Test
def void testDocumentFormatting() {
'''
{
"jsonrpc": "2.0",
"id": "12",
"method": "textDocument/formatting",
"params": {
"textDocument": {
"uri": "file:///tmp/foo"
},
"options": {
"insertSpaces": false,
"tabSize": 4,
"customProperty": -7
}
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = "12"
method = MessageMethods.DOC_FORMATTING
params = new DocumentFormattingParams => [
textDocument = new TextDocumentIdentifier("file:///tmp/foo")
options = new FormattingOptions => [
insertSpaces = false
putNumber('tabSize', new MyInteger(4))
putNumber('customProperty', new MyInteger(-7))
]
]
])
}
@Test
def void testMessageIssue() {
val issue = jsonHandler.gson.fromJson('''
{
"text": "Howdy!",
"code": 1234,
"cause": {
"message": "Foo",
"cause": {
"message": "Bar"
}
}
}
''', MessageIssue)
assertEquals('Howdy!', issue.text)
assertEquals(1234, issue.issueCode)
assertEquals('Foo', issue.cause.message)
assertEquals('Bar', issue.cause.cause.message)
}
@Test
def void testInitialize() {
'''
{
"jsonrpc": "2.0",
"id": "1",
"method": "initialize",
"params": {
"rootUri": "file:///tmp/foo"
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = "1"
method = MessageMethods.INITIALIZE
params = new InitializeParams => [
rootUri = "file:///tmp/foo"
]
])
}
@Test
def void testInitializeClientCapabilities() {
'''
{
"jsonrpc": "2.0",
"id": "1",
"method": "initialize",
"params": {
"rootUri": "file:///tmp/foo",
"capabilities": {
"textDocument": {
"synchronization": {
"dynamicRegistration": false,
"willSave": true,
"willSaveWaitUntil": false,
"didSave": true
},
"completion": {
"dynamicRegistration": false,
"completionItem": {
"snippetSupport": true,
"commitCharactersSupport": true,
"documentationFormat": ["plaintext", "markdown"]
},
"completionItemKind": {
"valueSet": [2, 3]
},
"contextSupport": false
},
"hover": {
"dynamicRegistration": false,
"contentFormat": ["plaintext", "markdown"]
},
"signatureHelp": {
"dynamicRegistration": false,
"signatureInformation": {
"documentationFormat": ["plaintext", "markdown"]
}
},
"references": {
"dynamicRegistration": false
},
"documentHighlight": {
"dynamicRegistration": false
},
"documentSymbol": {
"dynamicRegistration": false,
"symbolKind": {
valueSet: [2, 3, 4, 5]
}
},
"formatting": {
"dynamicRegistration": false
},
"rangeFormatting": {
"dynamicRegistration": false
},
"onTypeFormatting": {
"dynamicRegistration": false
},
"definition": {
"dynamicRegistration": false
},
"typeDefinition": {
"dynamicRegistration": false
},
"implementation": {
"dynamicRegistration": false
},
"codeAction": {
"dynamicRegistration": false
},
"codeLens": {
"dynamicRegistration": false
},
"documentLink": {
"dynamicRegistration": false
},
"colorProvider": {
"dynamicRegistration": false
},
"rename": {
"dynamicRegistration": false
}
},
"workspace": {}
}
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = "1"
method = MessageMethods.INITIALIZE
params = new InitializeParams => [
rootUri = "file:///tmp/foo"
capabilities = new ClientCapabilities => [
textDocument = new TextDocumentClientCapabilities => [
synchronization = new SynchronizationCapabilities => [
dynamicRegistration = false
willSave= true
willSaveWaitUntil= false
didSave = true
]
completion = new CompletionCapabilities => [
dynamicRegistration = false
completionItem = new CompletionItemCapabilities => [
snippetSupport = true
commitCharactersSupport = true
documentationFormat = #[MarkupKind.PLAINTEXT, MarkupKind.MARKDOWN]
]
completionItemKind = new CompletionItemKindCapabilities => [
valueSet = #[CompletionItemKind.Method, CompletionItemKind.Function]
]
contextSupport = false
]
hover = new HoverCapabilities => [
dynamicRegistration = false
contentFormat = #[MarkupKind.PLAINTEXT, MarkupKind.MARKDOWN]
]
signatureHelp = new SignatureHelpCapabilities => [
dynamicRegistration = false
signatureInformation = new SignatureInformationCapabilities => [
documentationFormat = #[MarkupKind.PLAINTEXT, MarkupKind.MARKDOWN]
]
]
references = new ReferencesCapabilities => [
dynamicRegistration = false
]
documentHighlight = new DocumentHighlightCapabilities => [
dynamicRegistration = false
]
documentSymbol = new DocumentSymbolCapabilities => [
dynamicRegistration = false
symbolKind = new SymbolKindCapabilities => [
valueSet = #[SymbolKind.Module, SymbolKind.Namespace, SymbolKind.Package, SymbolKind.Class]
]
]
formatting = new FormattingCapabilities => [
dynamicRegistration = false
]
rangeFormatting = new RangeFormattingCapabilities => [
dynamicRegistration = false
]
onTypeFormatting = new OnTypeFormattingCapabilities => [
dynamicRegistration = false
]
definition= new DefinitionCapabilities => [
dynamicRegistration = false
]
typeDefinition= new TypeDefinitionCapabilities => [
dynamicRegistration = false
]
implementation= new ImplementationCapabilities => [
dynamicRegistration = false
]
codeAction = new CodeActionCapabilities => [
dynamicRegistration = false
]
codeLens= new CodeLensCapabilities => [
dynamicRegistration = false
]
documentLink= new DocumentLinkCapabilities => [
dynamicRegistration = false
]
colorProvider = new ColorProviderCapabilities => [
dynamicRegistration = false
]
rename = new RenameCapabilities => [
dynamicRegistration = false
]
]
workspace = new WorkspaceClientCapabilities
]
]
])
}
@Test
def void testProgressCreate() {
'''
{
"jsonrpc": "2.0",
"id": 1,
"method": "window/workDoneProgress/create",
"params": {
"token": "progress-token"
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = 1
method = MessageMethods.PROGRESS_CREATE
params = new WorkDoneProgressCreateParams => [
token = Either.forLeft("progress-token")
]
])
'''
{
"jsonrpc": "2.0",
"id": 1,
"method": "window/workDoneProgress/create",
"params": {
"token": 1234
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = 1
method = MessageMethods.PROGRESS_CREATE
params = new WorkDoneProgressCreateParams => [
token = Either.forRight(1234)
]
])
}
@Test
def void testProgressCancel() {
'''
{
"jsonrpc": "2.0",
"id": 1,
"method": "window/workDoneProgress/cancel",
"params": {
"token": "progress-token"
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = 1
method = MessageMethods.PROGRESS_CANCEL
params = new WorkDoneProgressCancelParams => [
token = Either.forLeft("progress-token")
]
])
'''
{
"jsonrpc": "2.0",
"id": 1,
"method": "window/workDoneProgress/cancel",
"params": {
"token": 1234
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = 1
method = MessageMethods.PROGRESS_CANCEL
params = new WorkDoneProgressCancelParams => [
token = Either.forRight(1234)
]
])
}
@Test
def void testProgressNotify() {
'''
{
"jsonrpc": "2.0",
"id": 1,
"method": "$/progress",
"params": {
"token": "progress-token",
"value": {
"kind": "end",
"message": "message"
}
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = 1
method = MessageMethods.PROGRESS_NOTIFY
params = new ProgressParams => [
token = Either.forLeft("progress-token")
value = Either.<WorkDoneProgressNotification, Object>forLeft(new WorkDoneProgressEnd => [
message = "message"
])
]
])
'''
{
"jsonrpc": "2.0",
"id": 1,
"method": "$/progress",
"params": {
"token": 1234,
"value": {
"foo": "bar"
}
}
}
'''.assertParse(new RequestMessage => [
jsonrpc = "2.0"
id = 1
method = MessageMethods.PROGRESS_NOTIFY
params = new ProgressParams => [
token = Either.forRight(1234)
value = Either.<WorkDoneProgressNotification, Object>forRight(new JsonObject => [
addProperty("foo", "bar")
])
]
])
}
}