Skip to content

Commit

Permalink
Merge pull request #75215 from dotnet/dev/jorobich/fix-code-hover
Browse files Browse the repository at this point in the history
Allow <code> in DocComments to render as a block in Hover response
  • Loading branch information
JoeRobich authored Sep 25, 2024
2 parents ea092b5 + bd5499a commit acd83f7
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 5 deletions.
28 changes: 23 additions & 5 deletions src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.NavigateTo;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.SpellCheck;
using Microsoft.CodeAnalysis.Tags;
Expand Down Expand Up @@ -936,12 +935,23 @@ public static LSP.MarkupContent GetDocumentationMarkupContent(ImmutableArray<Tag

codeFence = null;

break;
case TextTags.Text when taggedText.Style == (TaggedTextStyle.Code | TaggedTextStyle.PreserveWhitespace):
// This represents a block of code (`<code></code>`) in doc comments.
// Since code elements optionally support a `lang` attribute and we do not have access to the
// language which was specified at this point, we tell the client to render it as plain text.

if (!markdownBuilder.IsLineEmpty())
AppendLineBreak(markdownBuilder);

// The current line is empty, we can append a code block.
markdownBuilder.AppendLine($"{BlockCodeFence}text");
markdownBuilder.AppendLine(taggedText.Text);
markdownBuilder.AppendLine(BlockCodeFence);

break;
case TextTags.LineBreak:
// A line ending with double space and a new line indicates to markdown
// to render a single-spaced line break.
markdownBuilder.Append(" ");
markdownBuilder.AppendLine();
AppendLineBreak(markdownBuilder);
break;
default:
var styledText = GetStyledText(taggedText, codeFence != null);
Expand All @@ -958,6 +968,14 @@ public static LSP.MarkupContent GetDocumentationMarkupContent(ImmutableArray<Tag
Value = content,
};

static void AppendLineBreak(MarkdownContentBuilder markdownBuilder)
{
// A line ending with double space and a new line indicates to markdown
// to render a single-spaced line break.
markdownBuilder.Append(" ");
markdownBuilder.AppendLine();
}

static string GetCodeBlockLanguageName(string language)
{
return language switch
Expand Down
42 changes: 42 additions & 0 deletions src/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,48 @@ void A.AMethod(int i)
Assert.Equal(expectedMarkdown, results.Contents.Fourth.Value);
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/75181")]
public async Task TestGetHoverAsync_UsingMarkupContentDoesNotEscapeCodeBlock(bool mutatingLspWorkspace)
{
var markup =
@"class A
{
/// <summary>
/// <code>
/// if (true) {
/// Console.WriteLine(""hello"");
/// }
/// </code>
/// </summary>
void {|caret:AMethod|}(int i)
{
}
}";
var clientCapabilities = new LSP.ClientCapabilities
{
TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = [LSP.MarkupKind.Markdown] } }
};
await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, clientCapabilities);
var expectedLocation = testLspServer.GetLocations("caret").Single();

var expectedMarkdown = @"```csharp
void A.AMethod(int i)
```
```text
if (true) {
Console.WriteLine(""hello"");
}
```
";

var results = await RunGetHoverAsync(
testLspServer,
expectedLocation).ConfigureAwait(false);
Assert.Equal(expectedMarkdown, results.Contents.Fourth.Value);
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/6577")]
public async Task TestGetHoverAsync_UsesInlineCodeFencesInAwaitReturn(bool mutatingLspWorkspace)
{
Expand Down

0 comments on commit acd83f7

Please sign in to comment.