提交 3d614b9e 编写于 作者: D David Barbet

Add hover support for razor lsp

上级 9e48ecfa
......@@ -55,19 +55,16 @@ public HoverHandler()
// local functions
// TODO - This should return correctly formatted markdown from quick info.
// https://github.com/dotnet/roslyn/projects/45#card-20033878
// https://github.com/dotnet/roslyn/issues/43387
static string GetMarkdownString(QuickInfoItem info)
{
var stringBuilder = new StringBuilder();
var description = info.Sections.FirstOrDefault(s => QuickInfoSectionKinds.Description.Equals(s.Kind))?.Text ?? string.Empty;
var documentation = info.Sections.FirstOrDefault(s => QuickInfoSectionKinds.DocumentationComments.Equals(s.Kind))?.Text ?? string.Empty;
if (!string.IsNullOrEmpty(description))
foreach (var section in info.Sections)
{
stringBuilder.Append(description);
if (!string.IsNullOrEmpty(documentation))
if (!string.IsNullOrEmpty(section.Text))
{
stringBuilder.Append("\r\n> ").Append(documentation);
stringBuilder.Append(section.Text).Append("\r\n");
}
}
......
......@@ -30,7 +30,84 @@ public async Task TestGetHoverAsync()
}";
using var workspace = CreateTestWorkspace(markup, out var locations);
var expectedLocation = locations["caret"].Single();
var expected = CreateHover(expectedLocation, "string A.Method(int i)\r\n> A great method");
var expected = CreateHover(expectedLocation, "string A.Method(int i)\r\n A great method\r\n\r\nReturns:\r\n a string\r\n");
var results = await RunGetHoverAsync(workspace.CurrentSolution, expectedLocation).ConfigureAwait(false);
AssertJsonEquals(expected, results);
}
[Fact]
public async Task TestGetHoverAsync_WithExceptions()
{
var markup =
@"class A
{
/// <summary>
/// A great method
/// </summary>
/// <exception cref='System.NullReferenceException'>
/// Oh no!
/// </exception>
private string {|caret:Method|}(int i)
{
}
}";
using var workspace = CreateTestWorkspace(markup, out var locations);
var expectedLocation = locations["caret"].Single();
var expected = CreateHover(expectedLocation, "string A.Method(int i)\r\n A great method\r\n\r\nExceptions:\r\n System.NullReferenceException\r\n");
var results = await RunGetHoverAsync(workspace.CurrentSolution, expectedLocation).ConfigureAwait(false);
AssertJsonEquals(expected, results);
}
[Fact]
public async Task TestGetHoverAsync_WithRemarks()
{
var markup =
@"class A
{
/// <summary>
/// A great method
/// </summary>
/// <remarks>
/// Remarks are cool too.
/// </remarks>
private string {|caret:Method|}(int i)
{
}
}";
using var workspace = CreateTestWorkspace(markup, out var locations);
var expectedLocation = locations["caret"].Single();
var expected = CreateHover(expectedLocation, "string A.Method(int i)\r\n A great method\r\n\r\nRemarks are cool too.\r\n");
var results = await RunGetHoverAsync(workspace.CurrentSolution, expectedLocation).ConfigureAwait(false);
AssertJsonEquals(expected, results);
}
[Fact]
public async Task TestGetHoverAsync_WithList()
{
var markup =
@"class A
{
/// <summary>
/// A great method
/// <list type='bullet'>
/// <item>
/// <description>Item 1.</description>
/// </item>
/// <item>
/// <description>Item 2.</description>
/// </item>
/// </list>
/// </summary>
private string {|caret:Method|}(int i)
{
}
}";
using var workspace = CreateTestWorkspace(markup, out var locations);
var expectedLocation = locations["caret"].Single();
var expected = CreateHover(expectedLocation, "string A.Method(int i)\r\n A great method\r\n\r\n• Item 1.\r\n• Item 2.\r\n");
var results = await RunGetHoverAsync(workspace.CurrentSolution, expectedLocation).ConfigureAwait(false);
AssertJsonEquals(expected, results);
......
......@@ -26,6 +26,13 @@ internal abstract class AbstractLanguageServerClient : ILanguageClient
private readonly Workspace _workspace;
private InProcLanguageServer? _languageServer;
/// <summary>
/// Allows the implementation to specify if hover should be supported from this language client.
/// Can be removed as soon as LSP supports classifications in hover.
/// Tracking - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/918138/
/// </summary>
protected virtual bool SupportsHover => false;
/// <summary>
/// Gets the name of the language client (displayed to the user).
/// </summary>
......@@ -71,7 +78,7 @@ public Task<Connection> ActivateAsync(CancellationToken token)
Contract.ThrowIfFalse(_languageServer == null, "This language server has already been initialized");
var (clientStream, serverStream) = FullDuplexStream.CreatePair();
_languageServer = new InProcLanguageServer(serverStream, serverStream, _languageServerProtocol, _workspace, _diagnosticService, clientName: _diagnosticsClientName);
_languageServer = new InProcLanguageServer(serverStream, serverStream, _languageServerProtocol, _workspace, _diagnosticService, clientName: _diagnosticsClientName, SupportsHover);
return Task.FromResult(new Connection(clientStream, clientStream));
}
......
......@@ -39,15 +39,17 @@ internal class InProcLanguageServer
private readonly string? _clientName;
private readonly JsonRpc _jsonRpc;
private readonly LanguageServerProtocol _protocol;
private readonly bool _supportsHover;
private readonly Workspace _workspace;
private VSClientCapabilities? _clientCapabilities;
public InProcLanguageServer(Stream inputStream, Stream outputStream, LanguageServerProtocol protocol,
Workspace workspace, IDiagnosticService diagnosticService, string? clientName)
Workspace workspace, IDiagnosticService diagnosticService, string? clientName, bool supportsHover)
{
_protocol = protocol;
_workspace = workspace;
_supportsHover = supportsHover;
_jsonRpc = new JsonRpc(outputStream, inputStream, this);
_jsonRpc.StartListening();
......@@ -63,7 +65,7 @@ internal class InProcLanguageServer
/// The specification assures that the initialize request is sent only once.
/// </summary>
[JsonRpcMethod(Methods.InitializeName)]
public Task<InitializeResult> InitializeAsync(JToken input, CancellationToken cancellationToken)
public async Task<InitializeResult> InitializeAsync(JToken input, CancellationToken cancellationToken)
{
// The VS LSP protocol package changed the type of 'tagSupport' from bool to an object.
// Our version of the LSP protocol package is older and assumes that the type is bool, so deserialization fails.
......@@ -85,7 +87,11 @@ public Task<InitializeResult> InitializeAsync(JToken input, CancellationToken ca
// sends additional VS specific capabilities, so directly deserialize them into the VSClientCapabilities
// to avoid losing them.
_clientCapabilities = input["capabilities"].ToObject<VSClientCapabilities>(serializer);
return _protocol.InitializeAsync(_workspace.CurrentSolution, input.ToObject<InitializeParams>(serializer), _clientCapabilities, cancellationToken);
var serverCapabilities = await _protocol.InitializeAsync(_workspace.CurrentSolution, input.ToObject<InitializeParams>(serializer), _clientCapabilities, cancellationToken).ConfigureAwait(false);
// As soon as LSP supports classifications in hover, we can remove this and always advertise hover support.
// Tracking - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/918138/
serverCapabilities.Capabilities.HoverProvider = _supportsHover;
return serverCapabilities;
}
[JsonRpcMethod(Methods.InitializedName)]
......@@ -147,6 +153,13 @@ public Task<DocumentHighlight[]> GetTextDocumentDocumentHighlightsAsync(JToken i
return _protocol.GetDocumentHighlightAsync(_workspace.CurrentSolution, textDocumentPositionParams, _clientCapabilities, cancellationToken);
}
[JsonRpcMethod(Methods.TextDocumentHoverName)]
public Task<Hover> GetTextDocumentDocumentHoverAsync(JToken input, CancellationToken cancellationToken)
{
var textDocumentPositionParams = input.ToObject<TextDocumentPositionParams>();
return _protocol.GetHoverAsync(_workspace.CurrentSolution, textDocumentPositionParams, _clientCapabilities, cancellationToken);
}
[JsonRpcMethod(Methods.TextDocumentDocumentSymbolName)]
public Task<object[]> GetTextDocumentDocumentSymbolsAsync(JToken input, CancellationToken cancellationToken)
{
......
......@@ -32,6 +32,8 @@ internal class RazorLanguageClient : AbstractLanguageServerClient
{
public const string ClientName = "RazorCSharp";
protected override bool SupportsHover => true;
/// <summary>
/// Gets the name of the language client (displayed to the user).
/// </summary>
......
......@@ -378,7 +378,7 @@ static InProcLanguageServer CreateLanguageServer(Stream inputStream, Stream outp
{
var protocol = ((TestWorkspace)workspace).ExportProvider.GetExportedValue<LanguageServerProtocol>();
var languageServer = new InProcLanguageServer(inputStream, outputStream, protocol, workspace, mockDiagnosticService, clientName: "RazorCSharp");
var languageServer = new InProcLanguageServer(inputStream, outputStream, protocol, workspace, mockDiagnosticService, clientName: "RazorCSharp", supportsHover: false);
return languageServer;
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册