Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the Hover LSP provider to use the new quickinfo service #1870

Merged
merged 7 commits into from
Aug 2, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
using System.Threading;
using System.Threading.Tasks;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using OmniSharp.Models.TypeLookup;
using OmniSharp.Models;

namespace OmniSharp.LanguageServerProtocol.Handlers
{
Expand All @@ -16,14 +14,14 @@ class OmniSharpHoverHandler : HoverHandler
public static IEnumerable<IJsonRpcHandler> Enumerate(RequestHandlers handlers)
{
foreach (var (selector, handler) in handlers
.OfType<Mef.IRequestHandler<TypeLookupRequest, TypeLookupResponse>>())
.OfType<Mef.IRequestHandler<QuickInfoRequest, QuickInfoResponse>>())
if (handler != null)
yield return new OmniSharpHoverHandler(handler, selector);
}

private readonly Mef.IRequestHandler<TypeLookupRequest, TypeLookupResponse> _definitionHandler;
private readonly Mef.IRequestHandler<QuickInfoRequest, QuickInfoResponse> _definitionHandler;

public OmniSharpHoverHandler(Mef.IRequestHandler<TypeLookupRequest, TypeLookupResponse> definitionHandler, DocumentSelector documentSelector)
public OmniSharpHoverHandler(Mef.IRequestHandler<QuickInfoRequest, QuickInfoResponse> definitionHandler, DocumentSelector documentSelector)
: base(new HoverRegistrationOptions()
{
DocumentSelector = documentSelector
Expand All @@ -34,12 +32,11 @@ public OmniSharpHoverHandler(Mef.IRequestHandler<TypeLookupRequest, TypeLookupRe

public async override Task<Hover> Handle(HoverParams request, CancellationToken token)
{
var omnisharpRequest = new TypeLookupRequest()
var omnisharpRequest = new QuickInfoRequest()
{
FileName = Helpers.FromUri(request.TextDocument.Uri),
Column = Convert.ToInt32(request.Position.Character),
Line = Convert.ToInt32(request.Position.Line),
IncludeDocumentation = true
};

var omnisharpResponse = await _definitionHandler.Handle(omnisharpRequest);
Expand All @@ -48,7 +45,7 @@ public async override Task<Hover> Handle(HoverParams request, CancellationToken
{
// TODO: Range? We don't currently have that!
// Range =
Contents = new MarkedStringsOrMarkupContent(new Container<MarkedString>(Helpers.EscapeMarkdown(omnisharpResponse.Type), Helpers.EscapeMarkdown(omnisharpResponse.Documentation)))
Contents = new MarkedStringsOrMarkupContent(new MarkupContent() { Value = omnisharpResponse.Markdown })
333fred marked this conversation as resolved.
Show resolved Hide resolved
};
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/OmniSharp.LanguageServerProtocol/Helpers.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Models;
Expand Down Expand Up @@ -122,13 +121,6 @@ public static Range ToRange(OmniSharp.Models.V2.Range range)
};
}

public static string EscapeMarkdown(string markdown)
{
if (markdown == null)
return null;
return Regex.Replace(markdown, @"([\\`\*_\{\}\[\]\(\)#+\-\.!])", @"\$1");
}

private static readonly IDictionary<string, SymbolKind> Kinds = new Dictionary<string, SymbolKind>
{
{ OmniSharp.Models.V2.SymbolKinds.Class, SymbolKind.Class },
Expand Down
16 changes: 13 additions & 3 deletions src/OmniSharp.Roslyn.CSharp/Services/QuickInfoProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using OmniSharp.Mef;
using OmniSharp.Models;
using OmniSharp.Options;
using OmniSharp.Utilities;

#nullable enable

Expand Down Expand Up @@ -186,12 +187,12 @@ static void buildSectionAsMarkdown(QuickInfoSection section, StringBuilder strin
switch (current.Tag)
{
case TextTags.Text when !isInCodeBlock:
stringBuilder.Append(current.Text);
addText(current.Text);
break;

case TextTags.Text:
endBlock();
stringBuilder.Append(current.Text);
addText(current.Text);
break;

case TextTags.Space when isInCodeBlock:
Expand All @@ -203,14 +204,18 @@ static void buildSectionAsMarkdown(QuickInfoSection section, StringBuilder strin
stringBuilder.Append(current.Text);
break;

case TextTags.Punctuation when isInCodeBlock && current.Text != "`":
stringBuilder.Append(current.Text);
break;

case TextTags.Space:
case TextTags.Punctuation:
stringBuilder.Append(current.Text);
break;

case ContainerStart:
addNewline();
stringBuilder.Append(current.Text);
addText(current.Text);
break;

case ContainerEnd:
Expand Down Expand Up @@ -246,6 +251,11 @@ static void buildSectionAsMarkdown(QuickInfoSection section, StringBuilder strin

return;

void addText(string text)
{
stringBuilder.Append(MarkdownHelpers.Escape(text));
}

void addNewline()
{
if (isInCodeBlock)
Expand Down
16 changes: 16 additions & 0 deletions src/OmniSharp.Shared/Utilities/MarkdownHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Text.RegularExpressions;

namespace OmniSharp.Utilities
{
public static class MarkdownHelpers
{
private static Regex EscapeRegex = new Regex(@"([\\`\*_\{\}\[\]\(\)#+\-\.!])", RegexOptions.Compiled);

public static string Escape(string markdown)
{
if (markdown == null)
return null;
return EscapeRegex.Replace(markdown, @"\$1");
}
}
}
45 changes: 32 additions & 13 deletions tests/OmniSharp.Roslyn.CSharp.Tests/QuickInfoProviderFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public async Task TypesFromInlineAssemlbyReferenceContainDocumentation()
var controller = new QuickInfoProvider(workspace, new FormattingOptions(), null);
var response = await controller.Handle(new QuickInfoRequest { FileName = testFile.FileName, Line = position.Line, Column = position.Offset });

Assert.Equal("```csharp\nclass ClassLibraryWithDocumentation.DocumentedClass\n```\n\nThis class performs an important function.", response.Markdown);
Assert.Equal("```csharp\nclass ClassLibraryWithDocumentation.DocumentedClass\n```\n\nThis class performs an important function\\.", response.Markdown);
}

[Fact]
Expand Down Expand Up @@ -310,7 +310,7 @@ class testissue
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nYou may have some additional information about this class here.", response.Markdown);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nYou may have some additional information about this class here\\.", response.Markdown);
}

[Fact]
Expand All @@ -325,7 +325,7 @@ class testissue
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nChecks if object is tagged with the tag.", response.Markdown);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nChecks if object is tagged with the tag\\.", response.Markdown);
}

[Fact]
Expand All @@ -340,7 +340,7 @@ class testissue
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nReturns:\n\n Returns true if object is tagged with tag.", response.Markdown);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nReturns:\n\n Returns true if object is tagged with tag\\.", response.Markdown);
}

[Fact]
Expand Down Expand Up @@ -466,7 +466,7 @@ public string Na$$me
}
";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nstring Employee.Name { }\n```\n\nThe Name property represents the employee's name.\n\nValue:\n\n The Name property gets/sets the value of the string field, _name.", response.Markdown);
Assert.Equal("```csharp\nstring Employee.Name { }\n```\n\nThe Name property represents the employee's name\\.\n\nValue:\n\n The Name property gets/sets the value of the string field, \\_name\\.", response.Markdown);
}

[Fact]
Expand All @@ -481,7 +481,7 @@ public class TestClass
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class. `System.Console.WriteLine(string)` for information about output statements.", response.Markdown);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class\\. `System.Console.WriteLine(string)` for information about output statements\\.", response.Markdown);
}

[Fact]
Expand Down Expand Up @@ -542,7 +542,7 @@ public class TestClass
}
";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class.\n\n\n\nHere's how you could make a second paragraph in a description.", response.Markdown);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class\\.\n\n\n\nHere's how you could make a second paragraph in a description\\.", response.Markdown);
}

[Fact]
Expand All @@ -563,7 +563,7 @@ static void Main()
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class. `TestClass.Main()`", response.Markdown);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class\\. `TestClass.Main()`", response.Markdown);
}

[Fact]
Expand All @@ -580,7 +580,7 @@ class testissue
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nChecks if object is tagged with the tag.", response.Markdown);
Assert.Equal("```csharp\nbool testissue.Compare(int gameObject, string tagName)\n```\n\nChecks if object is tagged with the tag\\.", response.Markdown);
}

[Fact]
Expand All @@ -602,7 +602,7 @@ class testissue
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal(
"```csharp\nT[] testissue.Compare(int gameObject)\n```\n\nChecks if object is tagged with the tag.\n\nYou may have some additional information about this class here.\n\nReturns:\n\n Returns an array of type `T`.\n\n\n\nExceptions:\n\n `System.Exception`",
"```csharp\nT[] testissue.Compare(int gameObject)\n```\n\nChecks if object is tagged with the tag\\.\n\nYou may have some additional information about this class here\\.\n\nReturns:\n\n Returns an array of type `T`\\.\n\n\n\nExceptions:\n\n `System.Exception`",
response.Markdown);
}

Expand All @@ -618,7 +618,7 @@ public class TestClass
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class.", response.Markdown);
Assert.Equal("```csharp\nvoid TestClass.DoWork(int Int1)\n```\n\nDoWork is a method in the TestClass class\\.", response.Markdown);
}

[Fact]
Expand All @@ -634,7 +634,7 @@ public static bool Compare(int gam$$eObject, string tagName)
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\n(parameter) int gameObject\n```\n\nThe game object.", response.Markdown);
Assert.Equal("```csharp\n(parameter) int gameObject\n```\n\nThe game object\\.", response.Markdown);
}

[Fact]
Expand All @@ -650,7 +650,7 @@ public static bool Compare(int gameObject, string tag$$Name)
}
}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\n(parameter) string tagName\n```\n\nName of the tag.", response.Markdown);
Assert.Equal("```csharp\n(parameter) string tagName\n```\n\nName of the tag\\.", response.Markdown);
}

[Fact]
Expand Down Expand Up @@ -692,6 +692,25 @@ public static void Main()
Assert.Equal("```csharp\nvoid Program.B()\n```\n\nHello World", response.Markdown);
}

[Fact]
public async Task MarkdownInComment()
{
string content = @"
class Program
{
/// <summary>*This should be escaped*</summary>
public static void B() { }

public static void A()
{
B$$();
}

}";
var response = await GetTypeLookUpResponse(content);
Assert.Equal("```csharp\nvoid Program.B()\n```\n\n\\*This should be escaped\\*", response.Markdown);
}

private async Task<QuickInfoResponse> GetTypeLookUpResponse(string content)
{
TestFile testFile = new TestFile("dummy.cs", content);
Expand Down