TypeFox / ls-api

MOVED TO
https://github.com/eclipse/lsp4j
20 stars 8 forks source link

Cannot parse hover results #23

Closed mickaelistria closed 8 years ago

mickaelistria commented 8 years ago

I'm using the ls-api against a locally started Language Server for CSS or JSON, the ones used and included in VSCode, hacked to transport on stdin/stdout. It's working fine for completion and diagnostics, but I'm having troubles to get hover working. It seems like the return message doesn't get correctly parsed:

OUT: 2.0
{"id":"0","method":"initialize","params":{"rootPath":"/home/mistria/git/3dLocalizer","capabilities":{},"clientName":"Eclipse IDE"},"jsonrpc":"2.0"}
IN: 2.0
{"jsonrpc":"2.0","id":"0","result":{"capabilities":{"textDocumentSync":1,"completionProvider":{"resolveProvider":false},"hoverProvider":true,"documentSymbolProvider":true,"referencesProvider":true,"definitionProvider":true,"documentHighlightProvider":true,"codeActionProvider":true,"renameProvider":true}}}
OUT: 2.0
{"method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:/home/mistria/git/3dLocalizer/test.css","languageId":"css","version":0,"text":"@charset \"UTF-8\[...]tpadding:0px 0px 0px 0px;\n}\n"}},"jsonrpc":"2.0"}
OUT: 2.0
{"id":"1","method":"textDocument/hover","params":{"textDocument":{"uri":"file:/home/mistria/git/3dLocalizer/test.css"},"uri":"file:/home/mistria/git/3dLocalizer/test.css","position":{"line":7,"character":3}},"jsonrpc":"2.0"}
Could not parse response: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
io.typefox.lsapi.services.json.InvalidMessageException: Could not parse response: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseResponse(MessageJsonHandler.xtend:199)
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseMessage(MessageJsonHandler.xtend:142)
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseMessage(MessageJsonHandler.xtend:131)
    at io.typefox.lsapi.services.json.LanguageServerProtocol.handleMessage(LanguageServerProtocol.xtend:72)
    at io.typefox.lsapi.services.json.LanguageServerProtocol$IOHandler.handleMessage(LanguageServerProtocol.xtend:267)
    at io.typefox.lsapi.services.json.LanguageServerProtocol$IOHandler.run(LanguageServerProtocol.xtend:203)
    at io.typefox.lsapi.services.json.LanguageServerProtocol$IOHandler.run(LanguageServerProtocol.xtend:163)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
    at com.google.gson.Gson.fromJson(Gson.java:861)
    at com.google.gson.Gson.fromJson(Gson.java:926)
    at com.google.gson.Gson.fromJson(Gson.java:899)
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseResponse(MessageJsonHandler.xtend:188)
    ... 11 more
Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
    at com.google.gson.internal.bind.JsonTreeReader.expect(JsonTreeReader.java:139)
    at com.google.gson.internal.bind.JsonTreeReader.beginArray(JsonTreeReader.java:58)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:117)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:217)
    ... 15 more
error: Could not parse response: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING
OUT: 2.0
{"id":"1","error":{"code":-32600,"message":"Could not parse response: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING"},"jsonrpc":"2.0"}
java.util.concurrent.TimeoutException
    at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1771)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1915)
    at org.eclipse.languageserver.LSBasedHover.getHoverRegion(LSBasedHover.java:70)
    at org.eclipse.jface.text.TextViewerHoverManager.computeInformation(TextViewerHoverManager.java:138)
    at org.eclipse.jface.text.AbstractInformationControlManager.doShowInformation(AbstractInformationControlManager.java:1144)
    at org.eclipse.jface.text.AbstractHoverInformationControlManager$MouseTracker.mouseHover(AbstractHoverInformationControlManager.java:518)
    at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:209)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
    at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5219)
    at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1340)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4553)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4143)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1121)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1022)
    at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:150)
    at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:687)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
    at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:604)
    at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148)
    at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:138)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1519)
    at org.eclipse.equinox.launcher.Main.main(Main.java:1492)

Seems like the ls-api client doesn't handle the case "contents: MarkedString | MarkedString[];" for Hover results.

spoenemann commented 8 years ago

Maybe we have to put some more customization into the Gson parser.

mickaelistria commented 8 years ago

Or maybe have some more flexible parser, allowing unexpected fields and not necessarily failing on missing ones. For example, in that case, a more flexible parser would log the parsing error (ok so far), return a Hover object without MarkedString[] set, but which would allow to allow to fail back to the JSon object and let customer do something else. For example hover.getJSonObject().get("contents"). IMHO, this approach of giving a way to get the JSon object for a response/request could allow to more easily workaround such cases and be able to somehow support new fields without need to change code immediately.

svenefftinge commented 8 years ago

I think it should parse the MarkedString and turn it into a java list, as the specification defines MarkedString | MarkedString[] in Java there is no equivalent type, but we can map it to a list always.

mickaelistria commented 8 years ago

I now get

UT: 2.0
{"id":"1","method":"textDocument/hover","params":{"textDocument":{"uri":"file:/home/mistria/git/3dLocalizer/test.css"},"uri":"file:/home/mistria/git/3dLocalizer/test.css","position":{"line":12,"character":5}},"jsonrpc":"2.0"}
Could not parse response: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
io.typefox.lsapi.services.json.InvalidMessageException: Could not parse response: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseResponse(MessageJsonHandler.xtend:223)
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseMessage(MessageJsonHandler.xtend:160)
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseMessage(MessageJsonHandler.xtend:149)
    at io.typefox.lsapi.services.json.LanguageServerProtocol.handleMessage(LanguageServerProtocol.xtend:72)
    at io.typefox.lsapi.services.json.LanguageServerProtocol$IOHandler.handleMessage(LanguageServerProtocol.xtend:267)
    at io.typefox.lsapi.services.json.LanguageServerProtocol$IOHandler.run(LanguageServerProtocol.xtend:203)
    at io.typefox.lsapi.services.json.LanguageServerProtocol$IOHandler.run(LanguageServerProtocol.xtend:163)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:221)
    at io.typefox.lsapi.services.json.adapters.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.xtend:29)
    at io.typefox.lsapi.services.json.adapters.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.xtend:67)
    at io.typefox.lsapi.services.json.adapters.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.xtend)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:117)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:217)
    at com.google.gson.Gson.fromJson(Gson.java:861)
    at com.google.gson.Gson.fromJson(Gson.java:926)
    at com.google.gson.Gson.fromJson(Gson.java:899)
    at io.typefox.lsapi.services.json.MessageJsonHandler.parseResponse(MessageJsonHandler.xtend:210)
    ... 11 more
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
    at com.google.gson.internal.bind.JsonTreeReader.expect(JsonTreeReader.java:139)
    at com.google.gson.internal.bind.JsonTreeReader.beginObject(JsonTreeReader.java:70)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:210)
    ... 20 more
error: Could not parse response: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
OUT: 2.0
{"id":"1","error":{"code":-32600,"message":"Could not parse response: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING"},"jsonrpc":"2.0"}
java.util.concurrent.TimeoutException
    at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1771)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1915)
    at org.eclipse.languageserver.LSBasedHover.getHoverRegion(LSBasedHover.java:80)
    at org.eclipse.jface.text.TextViewerHoverManager.computeInformation(TextViewerHoverManager.java:138)
    at org.eclipse.jface.text.AbstractInformationControlManager.doShowInformation(AbstractInformationControlManager.java:1144)
    at org.eclipse.jface.text.AbstractHoverInformationControlManager$MouseTracker.mouseHover(AbstractHoverInformationControlManager.java:518)
    at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:209)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
    at org.eclipse.swt.widgets.Display.sendEvent(Display.java:5219)
    at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1340)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4553)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:4143)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1121)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1022)
    at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:150)
    at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:687)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
    at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:604)
    at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148)
    at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:138)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1519)
    at org.eclipse.equinox.launcher.Main.main(Main.java:1492)

Can we reopen this issue or should I create a new one?

mickaelistria commented 8 years ago

@akosyakov Can you please add the bug label?

mickaelistria commented 8 years ago

In general, I'm wondering whether ls-api could be more tolerant with this kind of issues: if result is expected to be an array, and is an object or a string, automatically wrapp it into a single-element collection.

mickaelistria commented 8 years ago

In https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#hover-request

type MarkedString = string | { language: string; value: string };
mickaelistria commented 8 years ago

So I tried to add a custom TypeAdapterFactory and JsonDeserializer to MessageJsonHandler.getDefaultGsonBuilder() but those always get ignored. Indeed, the TypeAdapterRuntimeTypeWrapper will always call the delegate ReflectiveTypeAdapterFactory rather than the one I set. Is there a way to force my deserializer to have higher priority? Or, for sure better, is it possible to add a specific rule to allow deserializing a String to MarkedString directly in ls-api? @spoenemann @svenefftinge

mickaelistria commented 8 years ago

See https://github.com/eclipselabs/eclipse-language-service/commit/fd67c62eb1b02b2d5b7c051db0cab98b12616791 for the workaround I did set up on Eclipse's side.

mickaelistria commented 8 years ago

Fixed with https://github.com/TypeFox/ls-api/commit/c7ad588a078d94ba4228b8ec7e95cd40d04f2c3c (tried it successfully for https://github.com/eclipselabs/eclipse-language-service )