未验证 提交 ab927255 编写于 作者: D David 提交者: GitHub

Merge pull request #46149 from mgoertz-msft/dev/mgoertz/lsp

XAML LSP Support
......@@ -66,6 +66,12 @@ public ImmutableArray<Document> GetDocuments(Uri documentUri)
Contract.ThrowIfNull(_currentSolution);
return _currentSolution.GetDocuments(documentUri);
}
public ImmutableArray<TextDocument> GetTextDocuments(Uri documentUri)
{
Contract.ThrowIfNull(_currentSolution);
return _currentSolution.GetTextDocuments(documentUri);
}
}
private class TestSpanMapperProvider : IDocumentServiceProvider
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Roslyn.Utilities;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.LanguageServer
{
internal abstract class AbstractRequestHandlerProvider
{
private readonly ImmutableDictionary<string, Lazy<IRequestHandler, IRequestHandlerMetadata>> _requestHandlers;
public AbstractRequestHandlerProvider(IEnumerable<Lazy<IRequestHandler, IRequestHandlerMetadata>> requestHandlers, string? languageName = null)
=> _requestHandlers = CreateMethodToHandlerMap(requestHandlers.Where(rh => rh.Metadata.LanguageName == languageName));
private static ImmutableDictionary<string, Lazy<IRequestHandler, IRequestHandlerMetadata>> CreateMethodToHandlerMap(IEnumerable<Lazy<IRequestHandler, IRequestHandlerMetadata>> requestHandlers)
{
var requestHandlerDictionary = ImmutableDictionary.CreateBuilder<string, Lazy<IRequestHandler, IRequestHandlerMetadata>>();
foreach (var lazyHandler in requestHandlers)
{
requestHandlerDictionary.Add(lazyHandler.Metadata.MethodName, lazyHandler);
}
return requestHandlerDictionary.ToImmutable();
}
public Task<ResponseType> ExecuteRequestAsync<RequestType, ResponseType>(string methodName, RequestType request, LSP.ClientCapabilities clientCapabilities,
string? clientName, CancellationToken cancellationToken) where RequestType : class
{
Contract.ThrowIfNull(request);
Contract.ThrowIfTrue(string.IsNullOrEmpty(methodName), "Invalid method name");
var handler = (IRequestHandler<RequestType, ResponseType>?)_requestHandlers[methodName]?.Value;
Contract.ThrowIfNull(handler, string.Format("Request handler not found for method {0}", methodName));
return handler.HandleRequestAsync(request, clientCapabilities, clientName, cancellationToken);
}
}
}
......@@ -20,12 +20,22 @@ namespace Microsoft.CodeAnalysis.LanguageServer
{
internal static class Extensions
{
public static Uri GetURI(this Document document)
public static Uri GetURI(this TextDocument document)
{
return ProtocolConversions.GetUriFromFilePath(document.FilePath);
}
public static ImmutableArray<Document> GetDocuments(this Solution solution, Uri documentUri)
{
return GetDocuments<Document>(solution, documentUri, (s, i) => s.GetRequiredDocument(i));
}
public static ImmutableArray<TextDocument> GetTextDocuments(this Solution solution, Uri documentUri)
{
return GetDocuments<TextDocument>(solution, documentUri, (s, i) => s.GetRequiredTextDocument(i));
}
private static ImmutableArray<T> GetDocuments<T>(this Solution solution, Uri documentUri, Func<Solution, DocumentId, T> getDocument) where T : TextDocument
{
// TODO: we need to normalize this. but for now, we check both absolute and local path
// right now, based on who calls this, solution might has "/" or "\\" as directory
......@@ -37,12 +47,22 @@ public static ImmutableArray<Document> GetDocuments(this Solution solution, Uri
documentIds = solution.GetDocumentIdsWithFilePath(documentUri.LocalPath);
}
return documentIds.SelectAsArray(id => solution.GetRequiredDocument(id));
return documentIds.SelectAsArray(id => getDocument(solution, id));
}
public static ImmutableArray<Document> GetDocuments(this ILspSolutionProvider solutionProvider, Uri uri, string? clientName)
{
var documents = solutionProvider.GetDocuments(uri);
return GetDocuments<Document>(solutionProvider, uri, (s, u, c) => s.GetDocuments(u), clientName);
}
public static ImmutableArray<TextDocument> GetTextDocuments(this ILspSolutionProvider solutionProvider, Uri uri, string? clientName)
{
return GetDocuments<TextDocument>(solutionProvider, uri, (s, u, c) => s.GetTextDocuments(u), clientName);
}
private static ImmutableArray<T> GetDocuments<T>(this ILspSolutionProvider solutionProvider, Uri uri, Func<ILspSolutionProvider, Uri, string?, ImmutableArray<T>> getDocuments, string? clientName) where T : TextDocument
{
var documents = getDocuments(solutionProvider, uri, clientName);
// If we don't have a client name, then we're done filtering
if (clientName == null)
......@@ -65,7 +85,17 @@ public static ImmutableArray<Document> GetDocuments(this ILspSolutionProvider so
public static Document? GetDocument(this ILspSolutionProvider solutionProvider, TextDocumentIdentifier documentIdentifier, string? clientName = null)
{
var documents = solutionProvider.GetDocuments(documentIdentifier.Uri, clientName);
return GetDocument<Document>(solutionProvider, documentIdentifier, (s, d, c) => s.GetDocuments(d, c), clientName);
}
public static TextDocument? GetTextDocument(this ILspSolutionProvider solutionProvider, TextDocumentIdentifier documentIdentifier, string? clientName = null)
{
return GetDocument<TextDocument>(solutionProvider, documentIdentifier, (s, d, c) => s.GetTextDocuments(d, c), clientName);
}
private static T? GetDocument<T>(this ILspSolutionProvider solutionProvider, TextDocumentIdentifier documentIdentifier, Func<ILspSolutionProvider, Uri, string?, ImmutableArray<T>> getDocuments, string? clientName = null) where T : TextDocument
{
var documents = getDocuments(solutionProvider, documentIdentifier.Uri, clientName);
if (documents.Length == 0)
{
......@@ -96,7 +126,7 @@ public static ImmutableArray<Document> GetDocuments(this ILspSolutionProvider so
return documents[0];
}
public static async Task<int> GetPositionFromLinePositionAsync(this Document document, LinePosition linePosition, CancellationToken cancellationToken)
public static async Task<int> GetPositionFromLinePositionAsync(this TextDocument document, LinePosition linePosition, CancellationToken cancellationToken)
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
return text.Lines.GetPosition(linePosition);
......
......@@ -15,7 +15,9 @@ internal class ExportLspMethodAttribute : ExportAttribute, IRequestHandlerMetada
{
public string MethodName { get; }
public ExportLspMethodAttribute(string methodName) : base(typeof(IRequestHandler))
public string LanguageName { get; }
public ExportLspMethodAttribute(string methodName, string languageName = null) : base(typeof(IRequestHandler))
{
if (string.IsNullOrEmpty(methodName))
{
......@@ -23,6 +25,7 @@ public ExportLspMethodAttribute(string methodName) : base(typeof(IRequestHandler
}
MethodName = methodName;
LanguageName = languageName;
}
}
}
......@@ -10,5 +10,10 @@ internal interface IRequestHandlerMetadata
/// Name of the LSP method to handle.
/// </summary>
string MethodName { get; }
/// <summary>
/// Name of the language for LSP method to handle (optional).
/// </summary>
string LanguageName { get; }
}
}
......@@ -19,6 +19,14 @@ internal interface ILspSolutionProvider
/// <returns>the documents in the correct workspace and solution context</returns>
ImmutableArray<Document> GetDocuments(Uri documentUri);
/// <summary>
/// Finds the workspace and solution containing the specified document URI
/// and returns the text documents in that context.
/// </summary>
/// <param name="documentUri">the document's file path URI.</param>
/// <returns>the text documents in the correct workspace and solution context</returns>
ImmutableArray<TextDocument> GetTextDocuments(Uri documentUri);
/// <summary>
/// Return the latest solution from the main workspace that we know about.
/// </summary>
......
......@@ -8,6 +8,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
......@@ -23,36 +24,13 @@ namespace Microsoft.CodeAnalysis.LanguageServer
/// </summary>
[Shared]
[Export(typeof(LanguageServerProtocol))]
internal sealed class LanguageServerProtocol
internal sealed class LanguageServerProtocol : AbstractRequestHandlerProvider
{
private readonly ImmutableDictionary<string, Lazy<IRequestHandler, IRequestHandlerMetadata>> _requestHandlers;
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public LanguageServerProtocol([ImportMany] IEnumerable<Lazy<IRequestHandler, IRequestHandlerMetadata>> requestHandlers)
=> _requestHandlers = CreateMethodToHandlerMap(requestHandlers);
private static ImmutableDictionary<string, Lazy<IRequestHandler, IRequestHandlerMetadata>> CreateMethodToHandlerMap(IEnumerable<Lazy<IRequestHandler, IRequestHandlerMetadata>> requestHandlers)
{
var requestHandlerDictionary = ImmutableDictionary.CreateBuilder<string, Lazy<IRequestHandler, IRequestHandlerMetadata>>();
foreach (var lazyHandler in requestHandlers)
{
requestHandlerDictionary.Add(lazyHandler.Metadata.MethodName, lazyHandler);
}
return requestHandlerDictionary.ToImmutable();
}
public Task<ResponseType> ExecuteRequestAsync<RequestType, ResponseType>(string methodName, RequestType request, LSP.ClientCapabilities clientCapabilities,
string? clientName, CancellationToken cancellationToken) where RequestType : class
: base(requestHandlers)
{
Contract.ThrowIfNull(request);
Contract.ThrowIfTrue(string.IsNullOrEmpty(methodName), "Invalid method name");
var handler = (IRequestHandler<RequestType, ResponseType>?)_requestHandlers[methodName]?.Value;
Contract.ThrowIfNull(handler, string.Format("Request handler not found for method {0}", methodName));
return handler.HandleRequestAsync(request, clientCapabilities, clientName, cancellationToken);
}
}
}
......@@ -36,6 +36,7 @@
<InternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices" />
<InternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.LiveShare" />
<InternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.LiveShare.UnitTests" />
<InternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.Xaml" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.ServiceHub" />
<InternalsVisibleTo Include="Roslyn.Services.Test.Utilities" />
<InternalsVisibleTo Include="Roslyn.VisualStudio.Next.UnitTests" />
......
......@@ -22,7 +22,7 @@ internal abstract class AbstractLanguageServerClient : ILanguageClient
{
private readonly string? _diagnosticsClientName;
private readonly IDiagnosticService _diagnosticService;
private readonly LanguageServerProtocol _languageServerProtocol;
private readonly AbstractRequestHandlerProvider _requestHandlerProvider;
private readonly Workspace _workspace;
private InProcLanguageServer? _languageServer;
......@@ -57,12 +57,12 @@ internal abstract class AbstractLanguageServerClient : ILanguageClient
/// </summary>
public event AsyncEventHandler<EventArgs>? StopAsync { add { } remove { } }
public AbstractLanguageServerClient(LanguageServerProtocol languageServerProtocol,
public AbstractLanguageServerClient(AbstractRequestHandlerProvider requestHandlerProvider,
VisualStudioWorkspace workspace,
IDiagnosticService diagnosticService,
string? diagnosticsClientName)
{
_languageServerProtocol = languageServerProtocol;
_requestHandlerProvider = requestHandlerProvider;
_workspace = workspace;
_diagnosticService = diagnosticService;
_diagnosticsClientName = diagnosticsClientName;
......@@ -73,7 +73,7 @@ public Task<Connection> ActivateAsync(CancellationToken token)
Contract.ThrowIfTrue(_languageServer?.Running == true, "The language server has not yet shutdown.");
var (clientStream, serverStream) = FullDuplexStream.CreatePair();
_languageServer = new InProcLanguageServer(serverStream, serverStream, _languageServerProtocol, _workspace,
_languageServer = new InProcLanguageServer(serverStream, serverStream, _requestHandlerProvider, _workspace,
_diagnosticService, clientName: _diagnosticsClientName);
return Task.FromResult(new Connection(clientStream, clientStream));
}
......
......@@ -36,7 +36,7 @@ internal class InProcLanguageServer
private readonly IDiagnosticService _diagnosticService;
private readonly string? _clientName;
private readonly JsonRpc _jsonRpc;
private readonly LanguageServerProtocol _protocol;
private readonly AbstractRequestHandlerProvider _requestHandlerProvider;
private readonly CodeAnalysis.Workspace _workspace;
private VSClientCapabilities _clientCapabilities;
......@@ -44,12 +44,12 @@ internal class InProcLanguageServer
public InProcLanguageServer(Stream inputStream,
Stream outputStream,
LanguageServerProtocol protocol,
AbstractRequestHandlerProvider requestHandlerProvider,
CodeAnalysis.Workspace workspace,
IDiagnosticService diagnosticService,
string? clientName)
{
_protocol = protocol;
_requestHandlerProvider = requestHandlerProvider;
_workspace = workspace;
var jsonMessageFormatter = new JsonMessageFormatter();
......@@ -79,7 +79,7 @@ public async Task<InitializeResult> InitializeAsync(InitializeParams initializeP
{
_clientCapabilities = (VSClientCapabilities)initializeParams.Capabilities;
var serverCapabilities = await _protocol.ExecuteRequestAsync<InitializeParams, InitializeResult>(Methods.InitializeName,
var serverCapabilities = await _requestHandlerProvider.ExecuteRequestAsync<InitializeParams, InitializeResult>(Methods.InitializeName,
initializeParams, _clientCapabilities, _clientName, cancellationToken).ConfigureAwait(false);
// Always support hover - if any LSP client for a content type advertises support,
......@@ -137,78 +137,78 @@ public Task ExitAsync(CancellationToken _)
[JsonRpcMethod(Methods.TextDocumentDefinitionName, UseSingleObjectParameterDeserialization = true)]
public Task<LSP.Location[]> GetTextDocumentDefinitionAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<TextDocumentPositionParams, LSP.Location[]>(Methods.TextDocumentDefinitionName,
=> _requestHandlerProvider.ExecuteRequestAsync<TextDocumentPositionParams, LSP.Location[]>(Methods.TextDocumentDefinitionName,
textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentRenameName, UseSingleObjectParameterDeserialization = true)]
public Task<WorkspaceEdit> GetTextDocumentRenameAsync(RenameParams renameParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<RenameParams, WorkspaceEdit>(Methods.TextDocumentRenameName,
=> _requestHandlerProvider.ExecuteRequestAsync<RenameParams, WorkspaceEdit>(Methods.TextDocumentRenameName,
renameParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentReferencesName, UseSingleObjectParameterDeserialization = true)]
public Task<VSReferenceItem[]> GetTextDocumentReferencesAsync(ReferenceParams referencesParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<ReferenceParams, VSReferenceItem[]>(Methods.TextDocumentReferencesName,
=> _requestHandlerProvider.ExecuteRequestAsync<ReferenceParams, VSReferenceItem[]>(Methods.TextDocumentReferencesName,
referencesParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentCompletionName, UseSingleObjectParameterDeserialization = true)]
public async Task<SumType<CompletionList, CompletionItem[]>> GetTextDocumentCompletionAsync(CompletionParams completionParams, CancellationToken cancellationToken)
// Convert to sumtype before reporting to work around https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1107698
=> await _protocol.ExecuteRequestAsync<CompletionParams, CompletionItem[]>(Methods.TextDocumentCompletionName,
=> await _requestHandlerProvider.ExecuteRequestAsync<CompletionParams, CompletionItem[]>(Methods.TextDocumentCompletionName,
completionParams, _clientCapabilities, _clientName, cancellationToken).ConfigureAwait(false);
[JsonRpcMethod(Methods.TextDocumentCompletionResolveName, UseSingleObjectParameterDeserialization = true)]
public Task<CompletionItem> ResolveCompletionItemAsync(CompletionItem completionItem, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<CompletionItem, CompletionItem>(Methods.TextDocumentCompletionResolveName,
=> _requestHandlerProvider.ExecuteRequestAsync<CompletionItem, CompletionItem>(Methods.TextDocumentCompletionResolveName,
completionItem, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentDocumentHighlightName, UseSingleObjectParameterDeserialization = true)]
public Task<DocumentHighlight[]> GetTextDocumentDocumentHighlightsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<TextDocumentPositionParams, DocumentHighlight[]>(Methods.TextDocumentDocumentHighlightName,
=> _requestHandlerProvider.ExecuteRequestAsync<TextDocumentPositionParams, DocumentHighlight[]>(Methods.TextDocumentDocumentHighlightName,
textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentHoverName, UseSingleObjectParameterDeserialization = true)]
public Task<Hover?> GetTextDocumentDocumentHoverAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<TextDocumentPositionParams, Hover?>(Methods.TextDocumentHoverName,
=> _requestHandlerProvider.ExecuteRequestAsync<TextDocumentPositionParams, Hover?>(Methods.TextDocumentHoverName,
textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentDocumentSymbolName, UseSingleObjectParameterDeserialization = true)]
public Task<object[]> GetTextDocumentDocumentSymbolsAsync(DocumentSymbolParams documentSymbolParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<DocumentSymbolParams, object[]>(Methods.TextDocumentDocumentSymbolName,
=> _requestHandlerProvider.ExecuteRequestAsync<DocumentSymbolParams, object[]>(Methods.TextDocumentDocumentSymbolName,
documentSymbolParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentFormattingName, UseSingleObjectParameterDeserialization = true)]
public Task<TextEdit[]> GetTextDocumentFormattingAsync(DocumentFormattingParams documentFormattingParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<DocumentFormattingParams, TextEdit[]>(Methods.TextDocumentFormattingName,
=> _requestHandlerProvider.ExecuteRequestAsync<DocumentFormattingParams, TextEdit[]>(Methods.TextDocumentFormattingName,
documentFormattingParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentOnTypeFormattingName, UseSingleObjectParameterDeserialization = true)]
public Task<TextEdit[]> GetTextDocumentFormattingOnTypeAsync(DocumentOnTypeFormattingParams documentOnTypeFormattingParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<DocumentOnTypeFormattingParams, TextEdit[]>(Methods.TextDocumentOnTypeFormattingName,
=> _requestHandlerProvider.ExecuteRequestAsync<DocumentOnTypeFormattingParams, TextEdit[]>(Methods.TextDocumentOnTypeFormattingName,
documentOnTypeFormattingParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentImplementationName, UseSingleObjectParameterDeserialization = true)]
public Task<LSP.Location[]> GetTextDocumentImplementationsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<TextDocumentPositionParams, LSP.Location[]>(Methods.TextDocumentImplementationName,
=> _requestHandlerProvider.ExecuteRequestAsync<TextDocumentPositionParams, LSP.Location[]>(Methods.TextDocumentImplementationName,
textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentRangeFormattingName, UseSingleObjectParameterDeserialization = true)]
public Task<TextEdit[]> GetTextDocumentRangeFormattingAsync(DocumentRangeFormattingParams documentRangeFormattingParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<DocumentRangeFormattingParams, TextEdit[]>(Methods.TextDocumentRangeFormattingName,
=> _requestHandlerProvider.ExecuteRequestAsync<DocumentRangeFormattingParams, TextEdit[]>(Methods.TextDocumentRangeFormattingName,
documentRangeFormattingParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.TextDocumentSignatureHelpName, UseSingleObjectParameterDeserialization = true)]
public Task<SignatureHelp> GetTextDocumentSignatureHelpAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<TextDocumentPositionParams, SignatureHelp>(Methods.TextDocumentSignatureHelpName,
=> _requestHandlerProvider.ExecuteRequestAsync<TextDocumentPositionParams, SignatureHelp>(Methods.TextDocumentSignatureHelpName,
textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(Methods.WorkspaceSymbolName, UseSingleObjectParameterDeserialization = true)]
public Task<SymbolInformation[]> GetWorkspaceSymbolsAsync(WorkspaceSymbolParams workspaceSymbolParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<WorkspaceSymbolParams, SymbolInformation[]>(Methods.WorkspaceSymbolName,
=> _requestHandlerProvider.ExecuteRequestAsync<WorkspaceSymbolParams, SymbolInformation[]>(Methods.WorkspaceSymbolName,
workspaceSymbolParams, _clientCapabilities, _clientName, cancellationToken);
[JsonRpcMethod(MSLSPMethods.ProjectContextsName, UseSingleObjectParameterDeserialization = true)]
public Task<ActiveProjectContexts?> GetProjectContextsAsync(GetTextDocumentWithContextParams textDocumentWithContextParams, CancellationToken cancellationToken)
=> _protocol.ExecuteRequestAsync<GetTextDocumentWithContextParams, ActiveProjectContexts?>(MSLSPMethods.ProjectContextsName,
=> _requestHandlerProvider.ExecuteRequestAsync<GetTextDocumentWithContextParams, ActiveProjectContexts?>(MSLSPMethods.ProjectContextsName,
textDocumentWithContextParams, _clientCapabilities, _clientName, cancellationToken);
#pragma warning disable VSTHRD100 // Avoid async void methods
......
......@@ -47,5 +47,18 @@ public ImmutableArray<Document> GetDocuments(Uri documentUri)
return documents;
}
public ImmutableArray<TextDocument> GetTextDocuments(Uri documentUri)
{
// First check the VS workspace for matching documents.
var documents = _visualStudioWorkspace.CurrentSolution.GetTextDocuments(documentUri);
if (documents.IsEmpty)
{
// If there's none in the VS workspace, then check the misc files workspace.
documents = _miscellaneousFilesWorkspace.CurrentSolution.GetTextDocuments(documentUri);
}
return documents;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using Microsoft.VisualStudio.Text.Adornments;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion
{
internal interface IXamlCompletionItem
{
string[] CommitCharacters { get; }
string DisplayText { get; }
string InsertText { get; }
string Detail { get; }
string FilterText { get; }
string SortText { get; }
bool? Preselect { get; }
XamlCompletionKind Kind { get; }
ClassifiedTextElement Description { get; }
ImageElement Icon { get; }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion
{
internal interface IXamlCompletionService : ILanguageService
{
Task<ImmutableArray<IXamlCompletionItem>> GetCompletionsAsync(TextDocument document, int offset, CancellationToken cancellationToken);
Task<ISymbol> GetSymbolAsync(TextDocument document, int offset, string label, CancellationToken cancellationToken);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion
{
/// <summary>
/// An enum to identify what kind of completion a given Completion
/// represents.
/// </summary>
public enum XamlCompletionKind
{
/// <summary>
/// The completion represents a value.
/// </summary>
Element,
/// <summary>
/// The completion represents an attribute.
/// </summary>
Attribute,
/// <summary>
/// The completion represents an attribute value.
/// </summary>
Value,
/// <summary>
/// The completion represents a property element.
/// </summary>
PropertyElement,
/// <summary>
/// The completion represents a XML namespace prefix.
/// </summary>
Prefix,
/// <summary>
/// The completion represents a an event.
/// </summary>
Event,
/// <summary>
/// The completion represents a comment;
/// </summary>
Comment,
/// <summary>
/// This completion represents a CDATA
/// </summary>
CData,
/// <summary>
/// The completion represents a processing instruction.
/// </summary>
ProcessingInstruction,
/// <summary>
/// The completion represents an end tag.
/// </summary>
EndTag,
/// <summary>
/// The completion represents a type prefix for an attached property
/// or property elements (i.e. "Grid.").
/// </summary>
TypePrefix,
/// <summary>
/// The completion is returned for event handler values indicating
/// that the language service expects the name of an event handler.
/// The description of the event handler is found in
/// EventDescription property.
/// </summary>
EventHandlerDescription,
/// <summary>
/// The completion represents the type of a MarkupExtension.
/// </summary>
MarkupExtensionClass,
/// <summary>
/// The completion represents the name of a MarkupExtension parameter.
/// </summary>
MarkupExtensionParameter,
/// <summary>
/// The completion represents the value of a MarkupExtension parameter.
/// </summary>
MarkupExtensionValue,
/// <summary>
/// The completion represents a type for an attached property in the Property Completion.
/// (i.e. "Grid.").
/// </summary>
Type,
/// <summary>
/// The completion represents a value for Property attribute in Styles. (These are direct DPs on TargetTypes).
/// (i.e. "Grid.Background").
/// </summary>
PropertyValue,
/// <summary>
/// The completion represents a value for Property attribute in Styles. (These are APs on types).
/// (i.e. "Grid.Row").
/// </summary>
AttachedPropertyValue,
/// <summary>
/// The completion represents a type within for Property attribute in Styles. (These are the types that for APs).
/// (i.e. "Grid.Row").
/// </summary>
AttachedPropertyTypePrefix,
/// <summary>
/// The completion represents a local resource.
/// </summary>
LocalResource,
/// <summary>
/// The completion represents a system resource.
/// </summary>
SystemResource,
/// <summary>
/// The completion represents a property from the schema generated from a data source.
/// </summary>
DataBoundProperty,
/// <summary>
/// The completion represents the name of an element in the current scope.
/// </summary>
ElementName,
/// <summary>
/// The completion represents a namespace value. For instance, xmlns:local="Completion"
/// </summary>
NamespaceValue,
/// <summary>
/// The completion represents a condition value in a namespace. For instance, xmlns:local="namespace?Completion"
/// </summary>
ConditionValue,
/// <summary>
/// The completion represents a conditional argument value in a namespace. For instance, xmlns:local="namespace?Condition(Completion)"
/// </summary>
ConditionalArgument,
/// <summary>
/// A completion that cannot legally be used, but is shown for sake of user-education or
/// completeness. Example would be the phone Pivot control in a shared Mercury XAML file:
/// this type cannot be legally used in a shared context, but we want to show it as it
/// is a core phone type.
/// </summary>
Unusable,
/// <summary>
/// The completion represents #region for XAML
/// </summary>
RegionStart,
/// <summary>
/// The completion represents #endregion for XAML
/// </summary>
RegionEnd,
/// <summary>
/// The completion represents a snippet for XAML
/// </summary>
Snippet,
/// <summary>
/// The completion represents a method
/// </summary>
Method,
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.QuickInfo
{
internal interface IXamlQuickInfoService : ILanguageService
{
Task<XamlQuickInfo> GetQuickInfoAsync(TextDocument document, int position, CancellationToken cancellationToken);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.QuickInfo
{
internal sealed class XamlQuickInfo
{
public TextSpan Span { get; }
public IEnumerable<TaggedText> Description { get; }
public ISymbol Symbol { get; }
private XamlQuickInfo(
TextSpan span,
IEnumerable<TaggedText> description,
ISymbol symbol)
{
Span = span;
Description = description;
Symbol = symbol;
}
public static XamlQuickInfo Create(
TextSpan span,
IEnumerable<TaggedText> description,
ISymbol symbol = null)
{
return new XamlQuickInfo(span, description, symbol);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Structure
{
internal interface IXamlStructureService : ILanguageService
{
Task<ImmutableArray<IXamlStructureTag>> GetStructureTagsAsync(TextDocument document, CancellationToken cancellationToken);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Structure
{
internal interface IXamlStructureTag
{
string Type { get; }
TextSpan TextSpan { get; }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Structure
{
internal static class XamlStructureTypes
{
// Trivia
public const string Comment = nameof(Comment);
public const string Region = nameof(Region);
// Top level declarations
public const string Namespaces = nameof(Namespaces);
public const string Type = nameof(Type);
public const string Member = nameof(Member);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.LanguageServer.Client;
using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService;
using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer;
using Microsoft.VisualStudio.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Xaml
{
[ContentType(ContentTypeNames.XamlContentType)]
[DisableUserExperience(disableUserExperience: true)] // Remove this when we are ready to use LSP everywhere
[Export(typeof(ILanguageClient))]
internal class XamlLanguageServerClient : AbstractLanguageServerClient
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, true)]
public XamlLanguageServerClient(XamlLanguageServerProtocol languageServerProtocol, VisualStudioWorkspace workspace, IDiagnosticService diagnosticService)
: base(languageServerProtocol, workspace, diagnosticService, diagnosticsClientName: null)
{
}
/// <summary>
/// Gets the name of the language client (displayed to the user).
/// </summary>
public override string Name => Resources.Xaml_Language_Server_Client;
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Extensions
{
internal static class SymbolExtensions
{
private static readonly SymbolDisplayFormat s_descriptionStyle =
new SymbolDisplayFormat(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
delegateStyle: SymbolDisplayDelegateStyle.NameAndSignature,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance | SymbolDisplayGenericsOptions.IncludeTypeConstraints,
parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeParamsRefOut,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers,
kindOptions: SymbolDisplayKindOptions.IncludeNamespaceKeyword | SymbolDisplayKindOptions.IncludeTypeKeyword);
public static async Task<IEnumerable<TaggedText>> GetDescriptionAsync(this ISymbol symbol, TextDocument document, int offset, CancellationToken cancellationToken)
{
if (symbol == null)
{
return Enumerable.Empty<TaggedText>();
}
var formatter = document.Project.LanguageServices.GetService<IDocumentationCommentFormattingService>();
if (formatter == null)
{
return Enumerable.Empty<TaggedText>();
}
// TODO: Should we get this from the code-behind document instead?
var codeDocument = document.Project.Documents.FirstOrDefault();
if (codeDocument == null)
{
return Enumerable.Empty<TaggedText>();
}
var semanticModel = await codeDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
if (semanticModel == null)
{
return Enumerable.Empty<TaggedText>();
}
var textContentBuilder = new List<TaggedText>();
textContentBuilder.AddRange(symbol.ToDisplayParts(s_descriptionStyle).ToTaggedText());
var documentation = symbol.GetDocumentationParts(semanticModel, offset, formatter, cancellationToken);
if (documentation.Any())
{
textContentBuilder.AddLineBreak();
textContentBuilder.AddRange(documentation);
}
return textContentBuilder;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Xaml;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
/// <summary>
/// Handle a completion request.
/// </summary>
[Shared]
[ExportLspMethod(Methods.TextDocumentCompletionName, StringConstants.XamlLanguageName)]
internal class CompletionHandler : AbstractRequestHandler<CompletionParams, CompletionItem[]>
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CompletionHandler(ILspSolutionProvider solutionProvider) : base(solutionProvider)
{
}
public override async Task<CompletionItem[]> HandleRequestAsync(CompletionParams request, ClientCapabilities clientCapabilities, string clientName, CancellationToken cancellationToken)
{
var document = SolutionProvider.GetTextDocument(request.TextDocument, clientName);
if (document == null)
{
return CreateErrorItem($"Cannot find document in solution!", request.TextDocument.Uri.ToString());
}
var completionService = document.Project.LanguageServices.GetRequiredService<IXamlCompletionService>();
var offset = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false);
var completions = await completionService.GetCompletionsAsync(document, offset, cancellationToken: cancellationToken).ConfigureAwait(false);
if (completions == null)
{
return Array.Empty<CompletionItem>();
}
return completions.Select(c => CreateCompletionItem(c, document.Id, request.Position)).ToArray();
}
private static CompletionItem CreateCompletionItem(IXamlCompletionItem xamlCompletion, DocumentId documentId, Position position)
=> new VSCompletionItem
{
Label = xamlCompletion.DisplayText,
CommitCharacters = xamlCompletion.CommitCharacters,
Detail = xamlCompletion.Detail,
InsertText = xamlCompletion.InsertText,
Preselect = xamlCompletion.Preselect,
SortText = xamlCompletion.SortText,
FilterText = xamlCompletion.FilterText,
Kind = GetItemKind(xamlCompletion.Kind),
Description = xamlCompletion.Description,
Icon = xamlCompletion.Icon,
Data = new CompletionResolveData { ProjectGuid = documentId.ProjectId.Id, DocumentGuid = documentId.Id, Position = position, DisplayText = xamlCompletion.DisplayText }
};
private static CompletionItem[] CreateErrorItem(string message, string details = null)
{
var item = new CompletionItem
{
Label = message,
Documentation = details,
InsertText = string.Empty,
Kind = CompletionItemKind.Text,
};
return new[] { item };
}
private static CompletionItemKind GetItemKind(XamlCompletionKind kind)
{
switch (kind)
{
case XamlCompletionKind.Element:
case XamlCompletionKind.ElementName:
case XamlCompletionKind.EndTag:
return CompletionItemKind.Class;
case XamlCompletionKind.Attribute:
case XamlCompletionKind.AttachedPropertyValue:
case XamlCompletionKind.PropertyElement:
case XamlCompletionKind.MarkupExtensionParameter:
case XamlCompletionKind.ConditionalArgument:
return CompletionItemKind.Property;
case XamlCompletionKind.MarkupExtensionValue:
case XamlCompletionKind.PropertyValue:
case XamlCompletionKind.NamespaceValue:
case XamlCompletionKind.ConditionValue:
case XamlCompletionKind.Value:
return CompletionItemKind.Value;
case XamlCompletionKind.Event:
case XamlCompletionKind.EventHandlerDescription:
return CompletionItemKind.Event;
case XamlCompletionKind.MarkupExtensionClass:
return CompletionItemKind.Method;
case XamlCompletionKind.Prefix:
return CompletionItemKind.Constant;
case XamlCompletionKind.Type:
case XamlCompletionKind.TypePrefix:
case XamlCompletionKind.AttachedPropertyTypePrefix:
return CompletionItemKind.TypeParameter;
case XamlCompletionKind.LocalResource:
return CompletionItemKind.Reference;
case XamlCompletionKind.SystemResource:
return CompletionItemKind.Reference;
case XamlCompletionKind.CData:
case XamlCompletionKind.Comment:
case XamlCompletionKind.ProcessingInstruction:
case XamlCompletionKind.RegionStart:
case XamlCompletionKind.RegionEnd:
return CompletionItemKind.Keyword;
case XamlCompletionKind.DataBoundProperty:
return CompletionItemKind.Variable;
case XamlCompletionKind.Snippet:
return CompletionItemKind.Snippet;
default:
Debug.Fail($"Unhandled {nameof(XamlCompletionKind)}: {Enum.GetName(typeof(XamlCompletionKind), kind)}");
return CompletionItemKind.Text;
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
internal class CompletionResolveData
{
public Guid ProjectGuid { get; set; }
public Guid DocumentGuid { get; set; }
public Position Position { get; set; }
public string DisplayText { get; set; }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Xaml;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion;
using Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Extensions;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.Text.Adornments;
using Newtonsoft.Json.Linq;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.CodeAnalysis.DocumentationComments;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
/// <summary>
/// Handle a completion resolve request to add description.
/// </summary>
[Shared]
[ExportLspMethod(LSP.Methods.TextDocumentCompletionResolveName, StringConstants.XamlLanguageName)]
internal class CompletionResolveHandler : AbstractRequestHandler<LSP.CompletionItem, LSP.CompletionItem>
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CompletionResolveHandler(ILspSolutionProvider solutionProvider) : base(solutionProvider)
{
}
public override async Task<LSP.CompletionItem> HandleRequestAsync(LSP.CompletionItem completionItem, LSP.ClientCapabilities clientCapabilities,
string? clientName, CancellationToken cancellationToken)
{
if (!(completionItem.Data is CompletionResolveData data))
{
data = ((JToken)completionItem.Data).ToObject<CompletionResolveData>();
}
var documentId = DocumentId.CreateFromSerialized(ProjectId.CreateFromSerialized(data.ProjectGuid), data.DocumentGuid);
var document = SolutionProvider.GetCurrentSolutionForMainWorkspace().GetAdditionalDocument(documentId);
if (document == null)
{
return completionItem;
}
int offset = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(data.Position), cancellationToken).ConfigureAwait(false);
var completionService = document.Project.LanguageServices.GetRequiredService<IXamlCompletionService>();
var symbol = await completionService.GetSymbolAsync(document, offset, completionItem.Label, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
{
return completionItem;
}
var description = await symbol.GetDescriptionAsync(document, offset, cancellationToken).ConfigureAwait(false);
var vsCompletionItem = CloneVSCompletionItem(completionItem);
vsCompletionItem.Description = new ClassifiedTextElement(description.Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text)));
return vsCompletionItem;
}
private static LSP.VSCompletionItem CloneVSCompletionItem(LSP.CompletionItem completionItem)
{
return new LSP.VSCompletionItem
{
AdditionalTextEdits = completionItem.AdditionalTextEdits,
Command = completionItem.Command,
CommitCharacters = completionItem.CommitCharacters,
Data = completionItem.Data,
Detail = completionItem.Detail,
Documentation = completionItem.Documentation,
FilterText = completionItem.FilterText,
InsertText = completionItem.InsertText,
InsertTextFormat = completionItem.InsertTextFormat,
Kind = completionItem.Kind,
Label = completionItem.Label,
SortText = completionItem.SortText,
TextEdit = completionItem.TextEdit
};
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Xaml;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Structure;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
[Shared]
[ExportLspMethod(Methods.TextDocumentFoldingRangeName, StringConstants.XamlLanguageName)]
internal class FoldingRangesHandler : AbstractRequestHandler<FoldingRangeParams, FoldingRange[]>
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public FoldingRangesHandler(ILspSolutionProvider solutionProvider) : base(solutionProvider)
{
}
public override async Task<FoldingRange[]> HandleRequestAsync(FoldingRangeParams request, ClientCapabilities clientCapabilities,
string? clientName, CancellationToken cancellationToken)
{
var foldingRanges = ArrayBuilder<FoldingRange>.GetInstance();
var document = SolutionProvider.GetTextDocument(request.TextDocument, clientName);
if (document == null)
{
return foldingRanges.ToArrayAndFree();
}
var xamlStructureService = document.Project.LanguageServices.GetService<IXamlStructureService>();
if (xamlStructureService == null)
{
return foldingRanges.ToArrayAndFree();
}
var structureTags = await xamlStructureService.GetStructureTagsAsync(document, cancellationToken).ConfigureAwait(false);
if (structureTags == null)
{
return foldingRanges.ToArrayAndFree();
}
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
foreach (var structureTag in structureTags)
{
var linePositionSpan = text.Lines.GetLinePositionSpan(structureTag.TextSpan);
var foldingRangeKind = structureTag.Type switch
{
XamlStructureTypes.Comment => (FoldingRangeKind?)FoldingRangeKind.Comment,
XamlStructureTypes.Namespaces => FoldingRangeKind.Imports,
XamlStructureTypes.Region => FoldingRangeKind.Region,
_ => null,
};
foldingRanges.Add(new FoldingRange()
{
StartLine = linePositionSpan.Start.Line,
StartCharacter = linePositionSpan.Start.Character,
EndLine = linePositionSpan.End.Line,
EndCharacter = linePositionSpan.End.Character,
Kind = foldingRangeKind
});
}
return foldingRanges.ToArrayAndFree();
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Xaml;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.LanguageServices.Xaml.Features.QuickInfo;
using Microsoft.VisualStudio.Text.Adornments;
using Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Extensions;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
[Shared]
[ExportLspMethod(Methods.TextDocumentHoverName, StringConstants.XamlLanguageName)]
internal class HoverHandler : AbstractRequestHandler<TextDocumentPositionParams, Hover?>
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public HoverHandler(ILspSolutionProvider solutionProvider) : base(solutionProvider)
{
}
public override async Task<Hover?> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities,
string? clientName, CancellationToken cancellationToken)
{
var document = SolutionProvider.GetTextDocument(request.TextDocument, clientName);
if (document == null)
{
return null;
}
var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false);
var quickInfoService = document.Project.LanguageServices.GetService<IXamlQuickInfoService>();
if (quickInfoService == null)
{
return null;
}
var info = await quickInfoService.GetQuickInfoAsync(document, position, cancellationToken).ConfigureAwait(false);
if (info == null)
{
return null;
}
var descriptionBuilder = new List<TaggedText>(info.Description);
var description = await info.Symbol.GetDescriptionAsync(document, position, cancellationToken).ConfigureAwait(false);
if (description.Any())
{
if (descriptionBuilder.Any())
{
descriptionBuilder.AddLineBreak();
}
description.Concat(description);
}
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
return new VSHover
{
Range = ProtocolConversions.TextSpanToRange(info.Span, text),
Contents = new MarkupContent
{
Kind = MarkupKind.Markdown,
Value = GetMarkdownString(descriptionBuilder)
},
RawContent = new ClassifiedTextElement(descriptionBuilder.Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text)))
};
// local functions
// TODO - This should return correctly formatted markdown from tagged text.
// https://github.com/dotnet/roslyn/issues/43387
static string GetMarkdownString(IEnumerable<TaggedText> description)
=> string.Join("\r\n", description.Select(section => section.Text).Where(text => !string.IsNullOrEmpty(text)));
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Xaml;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
[Shared]
[ExportLspMethod(Methods.InitializeName, StringConstants.XamlLanguageName)]
internal class InitializeHandler : IRequestHandler<InitializeParams, InitializeResult>
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public InitializeHandler()
{
}
public Task<InitializeResult> HandleRequestAsync(InitializeParams request, ClientCapabilities clientCapabilities, string? clientName, CancellationToken cancellationToken)
{
return Task.FromResult(new InitializeResult
{
Capabilities = new ServerCapabilities
{
CompletionProvider = new CompletionOptions { ResolveProvider = true, TriggerCharacters = new string[] { "<", " ", ":", ".", "=", "\"", "'", "{", ",", "(" } },
HoverProvider = true,
FoldingRangeProvider = new FoldingRangeProviderOptions { },
TextDocumentSync = new TextDocumentSyncOptions
{
Change = TextDocumentSyncKind.None
}
}
});
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Editor.Xaml;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer
{
/// <summary>
/// Implements the Language Server Protocol for XAML
/// </summary>
[Shared]
[Export(typeof(XamlLanguageServerProtocol))]
internal sealed class XamlLanguageServerProtocol : AbstractRequestHandlerProvider
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public XamlLanguageServerProtocol([ImportMany] IEnumerable<Lazy<IRequestHandler, IRequestHandlerMetadata>> requestHandlers)
: base(requestHandlers, languageName: StringConstants.XamlLanguageName)
{
}
}
}
......@@ -15,6 +15,7 @@
<ProjectReference Include="..\..\..\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj" />
<ProjectReference Include="..\..\..\EditorFeatures\Text\Microsoft.CodeAnalysis.EditorFeatures.Text.csproj" />
<ProjectReference Include="..\..\Core\Def\Microsoft.VisualStudio.LanguageServices.csproj" />
<ProjectReference Include="..\..\..\Features\LanguageServer\Protocol\Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.VisualStudio.DesignTools.XamlLanguageService" Key="$(VisualStudioKey)" WorkItem="https://github.com/dotnet/roslyn/issues/35069" />
......
......@@ -126,4 +126,7 @@
<data name="Sort_Namespaces" xml:space="preserve">
<value>&amp;Sort Namespaces</value>
</data>
<data name="Xaml_Language_Server_Client" xml:space="preserve">
<value>XAML Language Server Client</value>
</data>
</root>
\ No newline at end of file
......@@ -3,13 +3,21 @@
// See the LICENSE file in the project root for more information.
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.LanguageServer.Client;
using Microsoft.VisualStudio.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Xaml
{
public static class XamlStaticTypeDefinitions
{
/// <summary>
/// Definition of the XAML content type.
/// </summary>
[Export]
[Name(ContentTypeNames.XamlContentType)]
[BaseDefinition(CodeRemoteContentDefinition.CodeRemoteContentTypeName)]
internal static readonly ContentTypeDefinition XamlContentType;
// Associate .xaml as the Xaml content type.
[Export]
[FileExtension(StringConstants.XamlFileExtension)]
......
......@@ -17,6 +17,11 @@
<target state="translated">&amp;Seřadit obory názvů</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">Namespaces &amp;sortieren</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">Orde&amp;nar espacios de nombres</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">&amp;Trier les espaces de noms</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">Or&amp;dina spazi dei nomi</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">名前空間の並べ替え(&amp;S)</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">네임 스페이스 정렬(&amp;S)</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">&amp;Sortuj przestrzenie nazw</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">Cla&amp;ssificar Namespaces</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">&amp;Сортировать пространства имен</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">&amp;Ad Alanlarını Sırala</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">对命名空间排序(&amp;S)</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -17,6 +17,11 @@
<target state="translated">排序 Namespace(&amp;S)</target>
<note />
</trans-unit>
<trans-unit id="Xaml_Language_Server_Client">
<source>XAML Language Server Client</source>
<target state="new">XAML Language Server Client</target>
<note />
</trans-unit>
</body>
</file>
</xliff>
\ No newline at end of file
......@@ -4,6 +4,7 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
......@@ -53,5 +54,27 @@ internal static Project WithSolutionOptions(this Project project, OptionSet opti
private static DocumentId? GetDocumentIdWithFilePath(this Project project, string filePath)
=> project.Solution.GetDocumentIdsWithFilePath(filePath).FirstOrDefault(id => id.ProjectId == project.Id);
public static Document GetRequiredDocument(this Project project, DocumentId documentId)
{
var document = project.GetDocument(documentId);
if (document == null)
{
throw new InvalidOperationException(WorkspaceExtensionsResources.The_solution_does_not_contain_the_specified_document);
}
return document;
}
public static TextDocument GetRequiredTextDocument(this Project project, DocumentId documentId)
{
var document = project.GetTextDocument(documentId);
if (document == null)
{
throw new InvalidOperationException(WorkspaceExtensionsResources.The_solution_does_not_contain_the_specified_document);
}
return document;
}
}
}
......@@ -55,9 +55,9 @@ public static Document GetRequiredDocument(this Solution solution, DocumentId do
return document;
}
public static Document GetRequiredDocument(this Project project, DocumentId documentId)
public static TextDocument GetRequiredTextDocument(this Solution solution, DocumentId documentId)
{
var document = project.GetDocument(documentId);
var document = solution.GetTextDocument(documentId);
if (document == null)
{
throw new InvalidOperationException(WorkspaceExtensionsResources.The_solution_does_not_contain_the_specified_document);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册