forked from OmniSharp/omnisharp-roslyn
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add documentation comment creation to the FormatAfterKeystrokeService
IDocumentationCommentSnippetService was recently moved down to the Features layer, which means omnisharp can now take advantage of it and generate documentation comment snippets on typing. There are a couple of caveats: 1. LSP's "/onTypingFormat" endpoint has no support for setting the cursor location after format, which means that the cursor ends up after the inserted block. This is fine for newlines inside doc comments, but awkward for the initial expansion. However, unless we want to add special support with a custom endpoint, we can't get around this (the custom endpoint is how Roslyn does it). 2. The service itself is still internal, so I had to add a few new reflection points. Server-side of dotnet/vscode-csharp#8. It also needs a vscode change, but it's a one-line change I'll submit later after we get this merged and I can write some integration tests.
- Loading branch information
Showing
4 changed files
with
597 additions
and
4 deletions.
There are no files selected for viewing
110 changes: 110 additions & 0 deletions
110
src/OmniSharp.Roslyn.CSharp/DocumentationComments/DocumentationCommentSnippetService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#nullable enable | ||
|
||
using System; | ||
using System.Reflection; | ||
using System.Threading; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Completion; | ||
using Microsoft.CodeAnalysis.Host; | ||
using Microsoft.CodeAnalysis.Options; | ||
using Microsoft.CodeAnalysis.Text; | ||
|
||
namespace OmniSharp.Roslyn.DocumentationComments | ||
{ | ||
/// <summary> | ||
/// Proxy service for Microsoft.CodeAnalysis.DocumentationComments.IDocumentationCommentSnippetService. | ||
/// Implementation was based on the service as of this commit: 2834b74995bb66a7cb19cb09069c17812819afdc | ||
/// See: https://github.com/dotnet/roslyn/blob/2834b74995bb66a7cb19cb09069c17812819afdc/src/Features/Core/Portable/DocumentationComments/IDocumentationCommentSnippetService.cs | ||
/// </summary> | ||
public struct DocumentationCommentSnippetService | ||
{ | ||
/// <summary> | ||
/// IDocumentationCommentService HostLanguageServices.GetRequiredService<IDocumentationCommentService>() | ||
/// </summary> | ||
private static MethodInfo s_getRequiredService; | ||
/// <summary> | ||
/// DocumentationCommentSnippet IDocumentationCommentService.GetDocumentationCommentSnippetOnCharacterTyped(SyntaxTree, SourceText, int, DocumentOptionSet, CancellationToken) | ||
/// </summary> | ||
private static MethodInfo s_getDocumentationCommentSnippetOnCharacterTyped; | ||
/// <summary> | ||
/// DocumentationCommentSnippet IDocumentationCommentService.GetDocumentationCommentSnippetOnEnterTyped(SyntaxTree, SourceText, int, DocumentOptionSet, CancellationToken) | ||
/// </summary> | ||
private static MethodInfo s_getDocumentationCommentSnippetOnEnterTyped; | ||
/// <summary> | ||
/// TextSpan DocumentationCommentSnippet.SpanToReplace | ||
/// </summary> | ||
private static PropertyInfo s_spanToReplace; | ||
/// <summary> | ||
/// string DocumentationCommentSnippet.SnippetText | ||
/// </summary> | ||
private static PropertyInfo s_snippetText; | ||
|
||
static DocumentationCommentSnippetService() | ||
{ | ||
var iDocumentationCommentSnippetServiceType = typeof(CompletionItem).Assembly.GetType("Microsoft.CodeAnalysis.DocumentationComments.IDocumentationCommentSnippetService"); | ||
s_getDocumentationCommentSnippetOnCharacterTyped = iDocumentationCommentSnippetServiceType.GetMethod(nameof(GetDocumentationCommentSnippetOnCharacterTyped)); | ||
s_getDocumentationCommentSnippetOnEnterTyped = iDocumentationCommentSnippetServiceType.GetMethod(nameof(GetDocumentationCommentSnippetOnEnterTyped)); | ||
|
||
var documentationCommentSnippet = typeof(CompletionItem).Assembly.GetType("Microsoft.CodeAnalysis.DocumentationComments.DocumentationCommentSnippet"); | ||
s_spanToReplace = documentationCommentSnippet.GetProperty(nameof(DocumentationCommentSnippet.SpanToReplace)); | ||
s_snippetText = documentationCommentSnippet.GetProperty(nameof(DocumentationCommentSnippet.SnippetText)); | ||
|
||
s_getRequiredService = typeof(HostLanguageServices).GetMethod(nameof(HostLanguageServices.GetRequiredService)).MakeGenericMethod(iDocumentationCommentSnippetServiceType); | ||
} | ||
|
||
public static DocumentationCommentSnippetService GetDocumentationCommentSnippetService(Document document) | ||
{ | ||
var service = s_getRequiredService.Invoke(document.Project.LanguageServices, Array.Empty<object>()); | ||
return new DocumentationCommentSnippetService(service); | ||
} | ||
|
||
private object _underlying; | ||
|
||
private DocumentationCommentSnippetService(object underlying) | ||
{ | ||
_underlying = underlying; | ||
} | ||
|
||
public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnCharacterTyped(SyntaxTree syntaxTree, SourceText text, int position, DocumentOptionSet options, CancellationToken cancellationToken) | ||
{ | ||
var originalSnippet = s_getDocumentationCommentSnippetOnCharacterTyped.Invoke(_underlying, new object[] { syntaxTree, text, position, options, cancellationToken }); | ||
return ConvertSnippet(originalSnippet); | ||
} | ||
|
||
public DocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped(SyntaxTree syntaxTree, SourceText text, int position, DocumentOptionSet options, CancellationToken cancellationToken) | ||
{ | ||
var originalSnippet = s_getDocumentationCommentSnippetOnEnterTyped.Invoke(_underlying, new object[] { syntaxTree, text, position, options, cancellationToken }); | ||
return ConvertSnippet(originalSnippet); | ||
} | ||
|
||
private static DocumentationCommentSnippet? ConvertSnippet(object? originalSnippet) | ||
{ | ||
if (originalSnippet == null) | ||
{ | ||
return null; | ||
} | ||
else | ||
{ | ||
return new DocumentationCommentSnippet((TextSpan)s_spanToReplace.GetValue(originalSnippet), (string)s_snippetText.GetValue(originalSnippet)); | ||
} | ||
} | ||
} | ||
|
||
public struct DocumentationCommentSnippet | ||
{ | ||
public TextSpan SpanToReplace { get; } | ||
public string SnippetText { get; } | ||
|
||
public DocumentationCommentSnippet(TextSpan spanToReplace, string snippetText) | ||
{ | ||
SpanToReplace = spanToReplace; | ||
SnippetText = snippetText; | ||
} | ||
} | ||
|
||
public static class WorkspaceExtensions | ||
{ | ||
public static DocumentationCommentSnippetService GetDocumentationCommentSnippetService(this Document document) | ||
=> DocumentationCommentSnippetService.GetDocumentationCommentSnippetService(document); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.