Visual Studio compatability
I'm creating a language server, and used this project to get the demo server & client extension up and running in visual Studio code successfully.
I'm also creating an extension for Visual Studio to reuse the language server, as per the instructions here: https://docs.microsoft.com/en-us/visualstudio/extensibility/adding-an-lsp-extension
In short, it looks like VS doesn't handle all the possible formats of the InitializeResult message.
In the spec, the hoverProvider in the server capabilities can be either boolean or HoverOptions, LanguageServer.Net is passing back a HoverOptions, but it looks like VS will only handle a boolean value at this time.
There are probably other instances of this kind of problem, this is the first I've come across.
Obviously, this is the responsibility of Visual Studio to implement the protocol properly, but in the meantime I can work around it by changing the message to a boolean rather than HoverOption. Is this the sort of thing that you'd like to do or accept a PR for, or shall I keep the changes to myself and wait for proper VS support?
The relevant bit of the log from visual studio:
<entry>
<record>2838</record>
<time>2020/05/22 09:57:46.925</time>
<type>Error</type>
<source>Language Extension</source>
<description>System.InvalidOperationException: Connection could not be established with the client. Client did not return a valid connnection. Client: Language Extension
 at Microsoft.VisualStudio.LanguageServer.Client.RemoteLanguageClientInstance.<InitializeAsync>d__76.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Threading.ThreadingTools.<WithCancellationSlow>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.LanguageServer.Client.RemoteLanguageClientInstance.<ActivateLanguageClientAsync>d__74.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)</description>
</entry>
<entry>
<record>2839</record>
<time>2020/05/22 09:57:48.190</time>
<type>Error</type>
<source>Language Extension</source>
<description>Newtonsoft.Json.JsonReaderException: Error reading boolean. Unexpected token: StartObject. Path 'hoverProvider'.
 at Newtonsoft.Json.JsonReader.ReadAsBoolean()
 at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
 at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
 at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
 at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType)
 at Newtonsoft.Json.Linq.JToken.ToObject[T]()
 at Microsoft.VisualStudio.LanguageServer.Protocol.VSExtensionConverter`2.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
 at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
 at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
 at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
 at Newtonsoft.Json.Linq.JToken.ToObject[T](JsonSerializer jsonSerializer)
 at StreamJsonRpc.JsonMessageFormatter.JsonRpcResult.GetResult[T]()
 at StreamJsonRpc.JsonRpc.<InvokeCoreAsync>d__116`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
 at Microsoft.VisualStudio.LanguageServer.Client.RemoteLanguageClientInstance.<InitializeAsync>d__76.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.Threading.ThreadingTools.<WithCancellationSlow>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.VisualStudio.LanguageServer.Client.RemoteLanguageClientInstance.<ActivateLanguageClientAsync>d__74.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
 at Microsoft.VisualStudio.Telemetry.WindowsErrorReporting.WatsonReport.GetClrWatsonExceptionInfo(Exception exceptionObject)</description>
</entry>
And the log of messages sent/received by the server:
2020-05-22 10:57:48.077 > {"id":2,"method":"initialize","params":{"processId":15340,"rootPath":"C:\\LocalCode\\Products.Enforcer\\src\\Tooling\\VSCode","rootUri":"file:///C:/LocalCode/Products.Enforcer/src/Tooling/VSCode","capabilities":{"supportsVisualStudioExtensions":true,"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true},"didChangeConfiguration":{"dynamicRegistration":false},"didChangeWatchedFiles":{"dynamicRegistration":false},"symbol":{"dynamicRegistration":false},"executeCommand":{"dynamicRegistration":false}},"textDocument":{"synchronization":{"willSave":false,"willSaveWaitUntil":false,"didSave":true,"dynamicRegistration":false},"completion":{"completionItem":{"snippetSupport":false,"commitCharactersSupport":true},"contextSupport":false,"dynamicRegistration":false},"hover":{"contentFormat":["plaintext"],"dynamicRegistration":false},"signatureHelp":{"signatureInformation":{"documentationFormat":["plaintext"],"parameterInformation":{"labelOffsetSupport":true}},"contextSupport":true,"dynamicRegistration":false},"references":{"dynamicRegistration":false},"documentHighlight":{"dynamicRegistration":false},"documentSymbol":{"dynamicRegistration":false},"formatting":{"dynamicRegistration":false},"rangeFormatting":{"dynamicRegistration":false},"onTypeFormatting":{"dynamicRegistration":false},"definition":{"dynamicRegistration":false},"implementation":{"dynamicRegistration":false},"typeDefinition":{"dynamicRegistration":false},"codeAction":{"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"dynamicRegistration":false},"codeLens":{"dynamicRegistration":false},"documentLink":{"dynamicRegistration":false},"rename":{"dynamicRegistration":false},"foldingRange":{"lineFoldingOnly":false,"dynamicRegistration":false},"publishDiagnostics":{"tagSupport":true}}},"trace":"off"},"jsonrpc":"2.0"}
2020-05-22 10:57:48.164 < {"id":2,"result":{"capabilities":{"textDocumentSync":{"openClose":true,"change":2,"willSave":true,"willSaveWaitUntil":false,"save":null},"hoverProvider":{"workDoneProgress":false},"completionProvider":{"resolveProvider":true,"triggerCharacters":".","workDoneProgress":false},"signatureHelpProvider":{"triggerCharacters":"()","workDoneProgress":false}}},"jsonrpc":"2.0"}
2020-05-22 10:57:48.329 > {"id":3,"method":"shutdown","jsonrpc":"2.0"}
2020-05-22 10:57:48.330 < {"id":3,"result":null,"jsonrpc":"2.0"}
2020-05-22 10:57:48.337 > {"method":"exit","jsonrpc":"2.0"}
2020-05-22 10:57:48.338 <
Exited
In the spec, the hoverProvider in the server capabilities can be either boolean or HoverOptions, LanguageServer.Net is passing back a HoverOptions, but it looks like VS will only handle a boolean value at this time.
I think currently the best we can do is to duplicate the contract and make some customizations. I'm still waiting for the presence of Union type (such as the one mentioned in dotnet/csharplang#399).
Is this the sort of thing that you'd like to do or accept a PR for, or shall I keep the changes to myself and wait for proper VS support?
As for contributing back the contract, I suppose it's better if we can separate the contract objects into different NuGet packages. Would you mind if set up a skeleton project specifically for VS and perhaps you can review the code and propse PR if there are further changes to make? I think a skeleton project without any classes inside is not illustrative enough.
Writing these contract is really a sort of labor, thus thanks in advance for your interest in contributing back!
Created LanguageServer.VisualStudio/Contracts/VsServerCapabilities.cs. I think the current contract should resolve the typing issue of HoverOptions already.
You may simple replace the usage of ServerCapabilities into VsServerCapabilities, and the serializer should make HoverOptions as a bool.
The package is yet to be published.