diff --git a/src/OmniSharp.Abstractions/Models/v2/CodeFoldingBlock.cs b/src/OmniSharp.Abstractions/Models/v2/CodeFoldingBlock.cs index b4e0633fdf..e8154d46c2 100644 --- a/src/OmniSharp.Abstractions/Models/v2/CodeFoldingBlock.cs +++ b/src/OmniSharp.Abstractions/Models/v2/CodeFoldingBlock.cs @@ -22,8 +22,8 @@ public CodeFoldingBlock(Range textSpan, string type) public class CodeFoldingBlockKinds { - public static readonly string Comment = nameof(Comment); - public static readonly string Imports = nameof(Imports); - public static readonly string Region = nameof(Region); + public const string Comment = nameof(Comment); + public const string Imports = nameof(Imports); + public const string Region = nameof(Region); } } diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpDocumentHighlightHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpDocumentHighlightHandler.cs new file mode 100644 index 0000000000..2a92e1d6cc --- /dev/null +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpDocumentHighlightHandler.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +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.Models; +using OmniSharp.Models.FindUsages; + +namespace OmniSharp.LanguageServerProtocol.Handlers +{ + internal sealed class OmniSharpDocumentHighlightHandler : DocumentHighlightHandlerBase + { + public static IEnumerable Enumerate(RequestHandlers handlers) + { + foreach (var (selector, handler) in handlers + .OfType>()) + if (handler != null) + yield return new OmniSharpDocumentHighlightHandler(handler, selector); + } + + private readonly Mef.IRequestHandler _findUsagesHandler; + private readonly DocumentSelector _documentSelector; + + public OmniSharpDocumentHighlightHandler(Mef.IRequestHandler findUsagesHandler, DocumentSelector documentSelector) + { + _findUsagesHandler = findUsagesHandler; + _documentSelector = documentSelector; + } + + public override async Task Handle(DocumentHighlightParams request, CancellationToken token) + { + // TODO: Utilize Roslyn ExternalAccess to take advantage of HighlightingService. + + var omnisharpRequest = new FindUsagesRequest + { + FileName = Helpers.FromUri(request.TextDocument.Uri), + Column = Convert.ToInt32(request.Position.Character), + Line = Convert.ToInt32(request.Position.Line), + OnlyThisFile = true, + ExcludeDefinition = false + }; + + var omnisharpResponse = await _findUsagesHandler.Handle(omnisharpRequest); + + if (omnisharpResponse.QuickFixes is null) + { + return new DocumentHighlightContainer(); + } + + return new DocumentHighlightContainer(omnisharpResponse.QuickFixes.Select(x => new DocumentHighlight + { + Kind = DocumentHighlightKind.Read, + Range = x.ToRange() + })); + } + + protected override DocumentHighlightRegistrationOptions CreateRegistrationOptions(DocumentHighlightCapability capability, ClientCapabilities clientCapabilities) + { + return new DocumentHighlightRegistrationOptions() + { + DocumentSelector = _documentSelector + }; + } + } +} diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpDocumentOnTypeFormattingHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpDocumentOnTypeFormattingHandler.cs index adbc82c588..7da882467f 100644 --- a/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpDocumentOnTypeFormattingHandler.cs +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpDocumentOnTypeFormattingHandler.cs @@ -56,9 +56,9 @@ protected override DocumentOnTypeFormattingRegistrationOptions CreateRegistratio return new DocumentOnTypeFormattingRegistrationOptions() { DocumentSelector = _documentSelector, - FirstTriggerCharacter = ";", - // TODO: What should these be? - MoreTriggerCharacter = new[] {"}", ")"} + // Chose these triggers based on Roslyn's implementation https://github.com/dotnet/roslyn/blob/9e06c76c5ce94dc49821c5bd211c8292b3a984f0/src/Features/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs#L71 + FirstTriggerCharacter = "}", + MoreTriggerCharacter = new[] { ";", "\n" } }; } } diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpFoldingRangeHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpFoldingRangeHandler.cs new file mode 100644 index 0000000000..c63353cad8 --- /dev/null +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpFoldingRangeHandler.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Linq; +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.Models.V2; +using static OmniSharp.LanguageServerProtocol.Helpers; + +namespace OmniSharp.LanguageServerProtocol.Handlers +{ + class OmniSharpFoldingRangenHandler : FoldingRangeHandlerBase + { + public static IEnumerable Enumerate(RequestHandlers handlers) + { + foreach (var (selector, handler) in handlers.OfType>()) + if (handler != null) + yield return new OmniSharpFoldingRangenHandler(handler, selector); + } + + private readonly Mef.IRequestHandler _definitionHandler; + private readonly DocumentSelector _documentSelector; + + public OmniSharpFoldingRangenHandler(Mef.IRequestHandler definitionHandler, DocumentSelector documentSelector) + { + _definitionHandler = definitionHandler; + _documentSelector = documentSelector; + } + + public override async Task> Handle(FoldingRangeRequestParam request, CancellationToken token) + { + var omnisharpRequest = new BlockStructureRequest() + { + FileName = FromUri(request.TextDocument.Uri) + }; + + var omnisharpResponse = await _definitionHandler.Handle(omnisharpRequest); + + if (omnisharpResponse.Spans is null) + { + return new Container(); + } + + return new Container(omnisharpResponse.Spans.Select(block => new FoldingRange() + { + StartLine = block.Range.Start.Line, + StartCharacter = block.Range.Start.Column, + EndLine = block.Range.End.Line, + EndCharacter = block.Range.End.Column, + Kind = ConvertKind(block.Kind), + })); + } + + private static FoldingRangeKind? ConvertKind(string kind) + { + return kind switch + { + CodeFoldingBlockKinds.Comment => FoldingRangeKind.Comment, + CodeFoldingBlockKinds.Imports => FoldingRangeKind.Imports, + CodeFoldingBlockKinds.Region => FoldingRangeKind.Region, + _ => null + }; + } + + protected override FoldingRangeRegistrationOptions CreateRegistrationOptions(FoldingRangeCapability capability, ClientCapabilities clientCapabilities) + { + return new FoldingRangeRegistrationOptions() + { + DocumentSelector = _documentSelector + }; + } + } +} diff --git a/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpSemanticTokensHandler.cs b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpSemanticTokensHandler.cs new file mode 100644 index 0000000000..6b3208a8b9 --- /dev/null +++ b/src/OmniSharp.LanguageServerProtocol/Handlers/OmniSharpSemanticTokensHandler.cs @@ -0,0 +1,106 @@ +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +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.Models.SemanticHighlight; +using OmniSharp.Roslyn.CSharp.Services.SemanticHighlight; +using static OmniSharp.LanguageServerProtocol.Helpers; + +namespace OmniSharp.LanguageServerProtocol.Handlers +{ + class OmniSharpSemanticTokensHandler : SemanticTokensHandlerBase + { + public static IEnumerable Enumerate(RequestHandlers handlers) + { + foreach (var (selector, handler) in handlers.OfType>()) + if (handler != null) + yield return new OmniSharpSemanticTokensHandler(handler, selector); + } + + private readonly Mef.IRequestHandler _definitionHandler; + private readonly DocumentSelector _documentSelector; + + private static readonly ImmutableDictionary _tokenTypes + = SemanticHighlightService._classificationMap + .OrderBy(kvp => kvp.Value) + .Aggregate( + new Dictionary(), + (dictionary, kvp) => + { + if (!dictionary.ContainsKey(kvp.Value)) + dictionary.Add(kvp.Value, new SemanticTokenType(kvp.Key)); + return dictionary; + }) + .ToImmutableDictionary(); + + private static readonly ImmutableDictionary _tokenModifiers + = SemanticHighlightService._modifierMap + .OrderBy(kvp => kvp.Value) + .Aggregate( + new Dictionary(), + (dictionary, kvp) => + { + if (!dictionary.ContainsKey(kvp.Value)) + dictionary.Add(kvp.Value, new SemanticTokenModifier(kvp.Key)); + return dictionary; + }) + .ToImmutableDictionary(); + + private readonly SemanticTokensLegend _legend = new() + { + TokenTypes = new Container(_tokenTypes.Values), + TokenModifiers = new Container(_tokenModifiers.Values), + }; + + public OmniSharpSemanticTokensHandler(Mef.IRequestHandler definitionHandler, DocumentSelector documentSelector) + { + _definitionHandler = definitionHandler; + _documentSelector = documentSelector; + } + + protected override async Task Tokenize(SemanticTokensBuilder builder, ITextDocumentIdentifierParams identifier, CancellationToken cancellationToken) + { + var omnisharpRequest = new SemanticHighlightRequest() + { + FileName = FromUri(identifier.TextDocument.Uri) + }; + + var omnisharpResponse = await _definitionHandler.Handle(omnisharpRequest); + + if (omnisharpResponse.Spans is null) + { + return; + } + + foreach (var span in omnisharpResponse.Spans) + { + var range = new Range(span.StartLine, span.StartColumn, span.EndLine, span.EndColumn); + builder.Push(range, _tokenTypes[span.Type], span.Modifiers.Select(modifier => _tokenModifiers[modifier])); + } + } + + protected override Task GetSemanticTokensDocument(ITextDocumentIdentifierParams @params, CancellationToken cancellationToken) + { + return Task.FromResult(new SemanticTokensDocument(_legend)); + } + + protected override SemanticTokensRegistrationOptions CreateRegistrationOptions(SemanticTokensCapability capability, ClientCapabilities clientCapabilities) + { + return new SemanticTokensRegistrationOptions() + { + DocumentSelector = _documentSelector, + Full = new SemanticTokensCapabilityRequestFull + { + Delta = false + }, + Range = true, + Legend = _legend + }; + } + } +} diff --git a/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs b/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs index 7fff1087a3..800d2f6a18 100644 --- a/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs +++ b/src/OmniSharp.LanguageServerProtocol/LanguageServerHost.cs @@ -370,22 +370,25 @@ internal static void RegisterHandlers(ILanguageServer server, CompositionHost co var serializer = server.Services.GetRequiredService(); server.Register(s => { - foreach (var handler in OmniSharpTextDocumentSyncHandler.Enumerate(handlers, workspace, documentVersions) - .Concat(OmniSharpTypeDefinitionHandler.Enumerate(handlers)) - .Concat(OmniSharpDefinitionHandler.Enumerate(handlers)) - .Concat(OmniSharpHoverHandler.Enumerate(handlers)) - .Concat(OmniSharpCompletionHandler.Enumerate(handlers)) - .Concat(OmniSharpSignatureHelpHandler.Enumerate(handlers)) - .Concat(OmniSharpRenameHandler.Enumerate(handlers)) - .Concat(OmniSharpWorkspaceSymbolsHandler.Enumerate(handlers)) - .Concat(OmniSharpDocumentSymbolHandler.Enumerate(handlers)) - .Concat(OmniSharpReferencesHandler.Enumerate(handlers)) - .Concat(OmniSharpImplementationHandler.Enumerate(handlers)) + foreach (var handler in OmniSharpCodeActionHandler.Enumerate(handlers, serializer, server, documentVersions) .Concat(OmniSharpCodeLensHandler.Enumerate(handlers)) - .Concat(OmniSharpCodeActionHandler.Enumerate(handlers, serializer, server, documentVersions)) + .Concat(OmniSharpCompletionHandler.Enumerate(handlers)) + .Concat(OmniSharpDefinitionHandler.Enumerate(handlers)) .Concat(OmniSharpDocumentFormattingHandler.Enumerate(handlers)) .Concat(OmniSharpDocumentFormatRangeHandler.Enumerate(handlers)) - .Concat(OmniSharpDocumentOnTypeFormattingHandler.Enumerate(handlers))) + .Concat(OmniSharpDocumentOnTypeFormattingHandler.Enumerate(handlers)) + .Concat(OmniSharpDocumentHighlightHandler.Enumerate(handlers)) + .Concat(OmniSharpDocumentSymbolHandler.Enumerate(handlers)) + .Concat(OmniSharpFoldingRangenHandler.Enumerate(handlers)) + .Concat(OmniSharpHoverHandler.Enumerate(handlers)) + .Concat(OmniSharpImplementationHandler.Enumerate(handlers)) + .Concat(OmniSharpReferencesHandler.Enumerate(handlers)) + .Concat(OmniSharpRenameHandler.Enumerate(handlers)) + .Concat(OmniSharpSemanticTokensHandler.Enumerate(handlers)) + .Concat(OmniSharpSignatureHelpHandler.Enumerate(handlers)) + .Concat(OmniSharpTextDocumentSyncHandler.Enumerate(handlers, workspace, documentVersions)) + .Concat(OmniSharpTypeDefinitionHandler.Enumerate(handlers)) + .Concat(OmniSharpWorkspaceSymbolsHandler.Enumerate(handlers))) { s.AddHandlers(handler); } diff --git a/src/OmniSharp.Roslyn.CSharp/Services/SemanticHighlight/SemanticHighlightService.cs b/src/OmniSharp.Roslyn.CSharp/Services/SemanticHighlight/SemanticHighlightService.cs index 66c7e9cbc4..ced0041b79 100644 --- a/src/OmniSharp.Roslyn.CSharp/Services/SemanticHighlight/SemanticHighlightService.cs +++ b/src/OmniSharp.Roslyn.CSharp/Services/SemanticHighlight/SemanticHighlightService.cs @@ -110,7 +110,7 @@ class ClassifiedResult public TextLineCollection Lines { get; set; } } - private static readonly Dictionary _classificationMap = + public static readonly Dictionary _classificationMap = new() { [ClassificationTypeNames.Comment] = SemanticHighlightClassification.Comment, @@ -182,7 +182,7 @@ class ClassifiedResult [ClassificationTypeNames.RegexOtherEscape] = SemanticHighlightClassification.RegexOtherEscape, }; - private static readonly Dictionary _modifierMap = + public static readonly Dictionary _modifierMap = new() { [ClassificationTypeNames.StaticSymbol] = SemanticHighlightModifier.Static,