提交 b22765ba 编写于 作者: M Marco Goertz

Added XAML LSP shims by introducing ILanguageServerProtocol and adding an...

Added XAML LSP shims by introducing ILanguageServerProtocol and adding an optional Language parameter to the ExportLspMethodHandlerAttribute so that XAML can export its own LSP handlers.
Added a few extension methods to make dealing with TextDocuments easier.
上级 41fa23e1
......@@ -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
......
......@@ -20,7 +20,7 @@ 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);
}
......@@ -96,7 +96,78 @@ 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 ImmutableArray<TextDocument> GetTextDocuments(this Solution solution, Uri documentUri)
{
// 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
// separator
var documentIds = solution.GetDocumentIdsWithFilePath(documentUri.AbsolutePath);
if (!documentIds.Any())
{
documentIds = solution.GetDocumentIdsWithFilePath(documentUri.LocalPath);
}
return documentIds.SelectAsArray(id => solution.GetRequiredTextDocument(id));
}
public static ImmutableArray<TextDocument> GetTextDocuments(this ILspSolutionProvider solutionProvider, Uri uri, string? clientName)
{
var documents = solutionProvider.GetTextDocuments(uri);
// If we don't have a client name, then we're done filtering
if (clientName == null)
{
return documents;
}
// We have a client name, so we need to filter to only documents that match that name
return documents.WhereAsArray(document =>
{
var documentPropertiesService = document.Services.GetService<DocumentPropertiesService>();
// When a client name is specified, only return documents that have a matching client name.
// Allows the razor lsp server to return results only for razor documents.
// This workaround should be removed when https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1106064/
// is fixed (so that the razor language server is only asked about razor buffers).
return Equals(documentPropertiesService?.DiagnosticsLspClientName, clientName);
});
}
public static TextDocument? GetTextDocument(this ILspSolutionProvider solutionProvider, TextDocumentIdentifier documentIdentifier, string? clientName = null)
{
var documents = solutionProvider.GetTextDocuments(documentIdentifier.Uri);
if (documents.Length == 0)
{
return null;
}
if (documents.Length > 1)
{
// We have more than one document; try to find the one that matches the right context
if (documentIdentifier is VSTextDocumentIdentifier vsDocumentIdentifier)
{
if (vsDocumentIdentifier.ProjectContext != null)
{
var projectId = ProtocolConversions.ProjectContextToProjectId(vsDocumentIdentifier.ProjectContext);
var matchingDocument = documents.FirstOrDefault(d => d.Project.Id == projectId);
if (matchingDocument != null)
{
return matchingDocument;
}
}
}
}
// We either have only one document or have multiple, but none of them matched our context. In the
// latter case, we'll just return the first one arbitrarily since this might just be some temporary mis-sync
// of client and server state.
return documents[0];
}
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; }
}
}
// 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.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.LanguageServer
{
internal interface ILanguageServerProtocol
{
Task<ResponseType> ExecuteRequestAsync<RequestType, ResponseType>(string methodName, RequestType request, ClientCapabilities clientCapabilities, string clientName, CancellationToken cancellationToken) where RequestType : class;
}
}
......@@ -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,14 +24,14 @@ namespace Microsoft.CodeAnalysis.LanguageServer
/// </summary>
[Shared]
[Export(typeof(LanguageServerProtocol))]
internal sealed class LanguageServerProtocol
internal sealed class LanguageServerProtocol : ILanguageServerProtocol
{
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);
=> _requestHandlers = CreateMethodToHandlerMap(requestHandlers.Where(rh => rh.Metadata.LanguageName == null));
private static ImmutableDictionary<string, Lazy<IRequestHandler, IRequestHandlerMetadata>> CreateMethodToHandlerMap(IEnumerable<Lazy<IRequestHandler, IRequestHandlerMetadata>> requestHandlers)
{
......
......@@ -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 ILanguageServerProtocol _languageServerProtocol;
private readonly Workspace _workspace;
private InProcLanguageServer? _languageServer;
......@@ -57,7 +57,7 @@ internal abstract class AbstractLanguageServerClient : ILanguageClient
/// </summary>
public event AsyncEventHandler<EventArgs>? StopAsync { add { } remove { } }
public AbstractLanguageServerClient(LanguageServerProtocol languageServerProtocol,
public AbstractLanguageServerClient(ILanguageServerProtocol languageServerProtocol,
VisualStudioWorkspace workspace,
IDiagnosticService diagnosticService,
string? diagnosticsClientName)
......
......@@ -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 ILanguageServerProtocol _protocol;
private readonly CodeAnalysis.Workspace _workspace;
private VSClientCapabilities _clientCapabilities;
......@@ -44,7 +44,7 @@ internal class InProcLanguageServer
public InProcLanguageServer(Stream inputStream,
Stream outputStream,
LanguageServerProtocol protocol,
ILanguageServerProtocol protocol,
CodeAnalysis.Workspace workspace,
IDiagnosticService diagnosticService,
string? clientName)
......@@ -98,7 +98,7 @@ public async Task InitializedAsync()
var openDocuments = _workspace.GetOpenDocumentIds();
foreach (var documentId in openDocuments)
{
var document = solution.GetDocument(documentId);
var document = solution.GetTextDocument(documentId);
if (document != null)
{
await PublishDiagnosticsAsync(document).ConfigureAwait(false);
......@@ -275,7 +275,7 @@ private async void DiagnosticService_DiagnosticsUpdated(object sender, Diagnosti
private static readonly Comparer<Uri> s_uriComparer = Comparer<Uri>.Create((uri1, uri2)
=> Uri.Compare(uri1, uri2, UriComponents.AbsoluteUri, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase));
internal async Task PublishDiagnosticsAsync(CodeAnalysis.Document document)
internal async Task PublishDiagnosticsAsync(CodeAnalysis.TextDocument document)
{
// Retrieve all diagnostics for the current document grouped by their actual file uri.
var fileUriToDiagnostics = await GetDiagnosticsAsync(document, CancellationToken.None).ConfigureAwait(false);
......@@ -348,7 +348,7 @@ private async Task SendDiagnosticsNotificationAsync(Uri uri, ImmutableArray<Lang
await _jsonRpc.NotifyWithParameterObjectAsync(Methods.TextDocumentPublishDiagnosticsName, publishDiagnosticsParams).ConfigureAwait(false);
}
private async Task<Dictionary<Uri, ImmutableArray<LanguageServer.Protocol.Diagnostic>>> GetDiagnosticsAsync(CodeAnalysis.Document document, CancellationToken cancellationToken)
private async Task<Dictionary<Uri, ImmutableArray<LanguageServer.Protocol.Diagnostic>>> GetDiagnosticsAsync(CodeAnalysis.TextDocument document, CancellationToken cancellationToken)
{
var diagnostics = _diagnosticService.GetDiagnostics(document.Project.Solution.Workspace, document.Project.Id, document.Id, null, false, cancellationToken)
.Where(IncludeDiagnostic);
......@@ -367,7 +367,7 @@ private async Task SendDiagnosticsNotificationAsync(Uri uri, ImmutableArray<Lang
group => group.Select(diagnostic => ConvertToLspDiagnostic(diagnostic, text)).ToImmutableArray());
return fileUriToDiagnostics;
static Uri GetDiagnosticUri(Document document, DiagnosticData diagnosticData)
static Uri GetDiagnosticUri(TextDocument document, DiagnosticData diagnosticData)
{
Contract.ThrowIfNull(diagnosticData.DataLocation, "Diagnostic data location should not be null here");
......
......@@ -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);
}
}
// -------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved.
// -------------------------------------------------------------------
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.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.ServiceHub.Client;
using Microsoft.VisualStudio.LanguageServer.Client;
using Microsoft.VisualStudio.LanguageServices.Remote;
using Microsoft.VisualStudio.Threading;
using Microsoft.VisualStudio.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageService
{
//[ContentType(ContentTypeNames.XamlContentType)]
//[Export(typeof(ILanguageClient))]
//[Export(typeof(LanguageServerClient))]
internal sealed class LanguageServerClient : ILanguageClient
{
private const string ServiceHubClientName = "Xaml.IDE.LanguageServer";
private readonly IThreadingContext _threadingContext;
private readonly HostWorkspaceServices _services;
private readonly IEnumerable<Lazy<IOptionPersister>> _lazyOptions;
/// <summary>
/// Gets the name of the language client (displayed to the user).
/// </summary>
public string Name => Resources.Xaml_Language_Server_Client;
/// <summary>
/// Gets the configuration section names for the language client. This may be null if the language client
/// does not provide settings.
/// </summary>
public IEnumerable<string> ConfigurationSections { get; }
/// <summary>
/// Gets the initialization options object the client wants to send when 'initialize' message is sent.
/// This may be null if the client does not need custom initialization options.
/// </summary>
public object InitializationOptions { get; }
/// <summary>
/// Gets the list of file names to watch for changes. Changes will be sent to the server via 'workspace/didChangeWatchedFiles'
/// message. The files to watch must be under the current active workspace. The file names can be specified as a relative
/// paths to the exact file, or as glob patterns following the standard in .gitignore see https://www.kernel.org/pub/software/scm/git/docs/gitignore.html files.
/// </summary>
public IEnumerable<string> FilesToWatch { get; }
public event AsyncEventHandler<EventArgs> StartAsync;
public event AsyncEventHandler<EventArgs> StopAsync { add { } remove { } }
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public LanguageServerClient(
IThreadingContext threadingContext,
VisualStudioWorkspace workspace,
[ImportMany] IEnumerable<Lazy<IOptionPersister>> lazyOptions)
{
_threadingContext = threadingContext;
_services = workspace.Services;
_lazyOptions = lazyOptions;
}
public async Task<Connection> ActivateAsync(CancellationToken cancellationToken)
{
var client = await RemoteHostClient.TryGetClientAsync(_services, cancellationToken).ConfigureAwait(false);
if (client == null)
{
// There is no OOP. either user turned it off, or process got killed.
// We should have already gotten a gold bar + nfw already if the OOP is missing.
// so just log telemetry here so we can connect the two with session explorer.
Logger.Log(FunctionId.LanguageServer_ActivateFailed, KeyValueLogMessage.NoProperty);
return null;
}
var hostGroup = new HostGroup(client.ClientId);
var hubClient = new HubClient(ServiceHubClientName);
var stream = await ServiceHubRemoteHostClient.RequestServiceAsync(
_services,
hubClient,
WellKnownServiceHubService.LanguageServer,
hostGroup,
cancellationToken).ConfigureAwait(false);
return new Connection(stream, stream);
}
/// <summary>
/// Signals that the extension has been loaded.
/// The caller expects that <see cref="ActivateAsync(CancellationToken)"/> can be called
/// immediately following the completion of this method.
/// </summary>
public async Task OnLoadedAsync()
{
// initialize things on UI thread
await InitializeOnUIAsync().ConfigureAwait(false);
// wait until remote host is available before let platform know that they can activate our LSP
var client = await RemoteHostClient.TryGetClientAsync(_services, CancellationToken.None).ConfigureAwait(false);
if (client == null)
{
// There is no OOP. either user turned it off, or process got killed.
// We should have already gotten a gold bar + nfw already if the OOP is missing.
// so just log telemetry here so we can connect the two with session explorer.
Logger.Log(FunctionId.LanguageServer_OnLoadedFailed, KeyValueLogMessage.NoProperty);
// don't ask platform to start LSP.
// we shouldn't throw as the LSP client does not expect exceptions here.
return;
}
// let platform know that they can start us
await StartAsync.InvokeAsync(this, EventArgs.Empty).ConfigureAwait(false);
async Task InitializeOnUIAsync()
{
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync();
// this doesn't attempt to solve our JTF and some services being not free-thread issue here, but
// try to fix this particular deadlock issue only. we already have long discussion on
// how we need to deal with JTF, Roslyn service requirements and VS services reality conflicting
// each others. architectural fix should come from the result of that discussion.
// Ensure the options persisters are loaded since we have to fetch options from the shell
_lazyOptions.Select(o => o.Value);
// experimentation service unfortunately uses JTF to jump to UI thread in certain cases
// which can cause deadlock if 2 parties try to enable OOP from BG and then FG before
// experimentation service tries to jump to UI thread.
var experimentationService = _services.GetService<IExperimentationService>();
}
}
/// <summary>
/// Signals the extension that the language server has been successfully initialized.
/// </summary>
public Task OnServerInitializedAsync()
=> Task.CompletedTask;
/// <summary>
/// Signals the extension that the language server failed to initialize.
/// </summary>
public Task OnServerInitializeFailedAsync(Exception e)
=> Task.CompletedTask;
}
}
// 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)]
[ClientName(ClientName)]
[DisableUserExperience(disableUserExperience: true)] // Remove this when we are ready to use LSP everywhere
[Export(typeof(ILanguageClient))]
internal class XamlLanguageServerClient : AbstractLanguageServerClient
{
public const string ClientName = "XAML";
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, true)]
public XamlLanguageServerClient(XamlLanguageServerProtocol languageServerProtocol, VisualStudioWorkspace workspace, IDiagnosticService diagnosticService)
: base(languageServerProtocol, workspace, diagnosticService, ClientName)
{
}
/// <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(),
//DocumentHighlightProvider = true,
//DocumentFormattingProvider = true,
//DocumentRangeFormattingProvider = true,
//DocumentOnTypeFormattingProvider = new LSP.DocumentOnTypeFormattingOptions { FirstTriggerCharacter = "}", MoreTriggerCharacter = new[] { ";", "\n" } },
//DefinitionProvider = true,
//ImplementationProvider = true,
//ReferencesProvider = true,
//ProjectContextProvider = true,
//RenameProvider = true,
//DocumentSymbolProvider = true,
//WorkspaceSymbolProvider = true,
//SignatureHelpProvider = new LSP.SignatureHelpOptions { TriggerCharacters = new[] { "{", "," } },
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.Collections.Immutable;
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 Roslyn.Utilities;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer
{
/// <summary>
/// Implements the Language Server Protocol for XAML
/// </summary>
[Shared]
[Export(typeof(XamlLanguageServerProtocol))]
internal sealed class XamlLanguageServerProtocol : ILanguageServerProtocol
{
private readonly ImmutableDictionary<string, Lazy<IRequestHandler, IRequestHandlerMetadata>> _requestHandlers;
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public XamlLanguageServerProtocol([ImportMany] IEnumerable<Lazy<IRequestHandler, IRequestHandlerMetadata>> requestHandlers)
=> _requestHandlers = CreateMethodToHandlerMap(requestHandlers.Where(rh => rh.Metadata.LanguageName == StringConstants.XamlLanguageName));
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);
}
}
}
......@@ -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.
先完成此消息的编辑!
想要评论请 注册