Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional XAML LSP handlers #47217

Merged
merged 3 commits into from
Aug 29, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ public Task<CompletionItem> ResolveCompletionItemAsync(CompletionItem completion
=> _requestHandlerProvider.ExecuteRequestAsync<CompletionItem, CompletionItem>(Methods.TextDocumentCompletionResolveName,
completionItem, _clientCapabilities, _clientName, cancellationToken);

[JsonRpcMethod(Methods.TextDocumentFoldingRangeName, UseSingleObjectParameterDeserialization = true)]
public Task<FoldingRange[]> GetTextDocumentFoldingRangeAsync(FoldingRangeParams textDocumentFoldingRangeParams, CancellationToken cancellationToken)
=> _requestHandlerProvider.ExecuteRequestAsync<FoldingRangeParams, FoldingRange[]>(Methods.TextDocumentFoldingRangeName,
textDocumentFoldingRangeParams, _clientCapabilities, _clientName, cancellationToken);

[JsonRpcMethod(Methods.TextDocumentDocumentHighlightName, UseSingleObjectParameterDeserialization = true)]
public Task<DocumentHighlight[]> GetTextDocumentDocumentHighlightsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
=> _requestHandlerProvider.ExecuteRequestAsync<TextDocumentPositionParams, DocumentHighlight[]>(Methods.TextDocumentDocumentHighlightName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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.AutoInsert
{
internal interface IXamlAutoInsertService : ILanguageService
{
public Task<XamlAutoInsertResult> GetAutoInsertAsync(TextDocument document, char typedChar, int position, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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 Microsoft.CodeAnalysis.Text;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.AutoInsert
{
internal class XamlAutoInsertResult
{
public TextChange TextChange { get; set; }
mgoertz-msft marked this conversation as resolved.
Show resolved Hide resolved
public int? CaretOffset { get; set; }
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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;
Expand All @@ -12,7 +11,7 @@ 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);
Task<XamlCompletionResult> GetCompletionsAsync(XamlCompletionContext completionContext, CancellationToken cancellationToken);
Task<ISymbol> GetSymbolAsync(XamlCompletionContext completionContext, string label, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion
{
/// <summary>
/// Contextual information needed for processing completion requests.
/// </summary>
internal struct XamlCompletionContext
{
public XamlCompletionContext(TextDocument document, int offset, char triggerChar = '\0')
{
Document = document;
Offset = offset;
TriggerChar = triggerChar;
}

public TextDocument Document { get; }
public int Offset { get; }
public char TriggerChar { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text.Adornments;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion
{
internal class XamlCompletionItem
{
public string[] CommitCharacters { get; set; }
public string DisplayText { get; set; }
public string InsertText { get; set; }
public string Detail { get; set; }
public string FilterText { get; set; }
public string SortText { get; set; }
public bool? Preselect { get; set; }
public TextSpan? Span { get; set; }
public XamlCompletionKind Kind { get; set; }
public ClassifiedTextElement Description { get; set; }
public ImageElement Icon { get; set; }
public ISymbol Symbol { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.CodeAnalysis.Text;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion
{
internal class XamlCompletionResult
{
public ImmutableArray<XamlCompletionItem> Completions { get; set; }
public TextSpan? ApplicableToSpan { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Formatting
{
internal interface IXamlFormattingService : ILanguageService
{
Task<IList<TextChange>> GetFormattingChangesAsync(TextDocument document, XamlFormattingOptions options, TextSpan? textSpan, CancellationToken cancellationToken);
Task<IList<TextChange>> GetFormattingChangesAsync(TextDocument document, XamlFormattingOptions options, char typedChar, int position, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Formatting
{
internal class XamlFormattingOptions
{
public bool InsertSpaces { get; set; }
public int TabSize { get; set; }
public IDictionary<string, object> OtherOptions { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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;
Expand All @@ -13,6 +12,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Structure
{
internal interface IXamlStructureService : ILanguageService
{
Task<ImmutableArray<IXamlStructureTag>> GetStructureTagsAsync(TextDocument document, CancellationToken cancellationToken);
Task<ImmutableArray<XamlStructureTag>> GetStructureTagsAsync(TextDocument document, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

namespace Microsoft.VisualStudio.LanguageServices.Xaml.Features.Structure
{
internal interface IXamlStructureTag
internal class XamlStructureTag
{
string Type { get; }
TextSpan TextSpan { get; }
public string Type { get; set; }
public TextSpan TextSpan { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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.Diagnostics;
Expand All @@ -13,6 +15,7 @@
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Completion;

Expand Down Expand Up @@ -40,23 +43,35 @@ public override async Task<CompletionItem[]> HandleRequestAsync(CompletionParams
}

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)
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var offset = text.Lines.GetPosition(ProtocolConversions.PositionToLinePosition(request.Position));
var completionResult = await completionService.GetCompletionsAsync(new XamlCompletionContext(document, offset, request.Context.TriggerCharacter?.FirstOrDefault() ?? '\0'), cancellationToken: cancellationToken).ConfigureAwait(false);
if (completionResult?.Completions == null)
{
return Array.Empty<CompletionItem>();
}

return completions.Select(c => CreateCompletionItem(c, document.Id, request.Position)).ToArray();
return completionResult.Completions.Select(c => CreateCompletionItem(c, document.Id, text, request.Position)).ToArray();
}

private static CompletionItem CreateCompletionItem(IXamlCompletionItem xamlCompletion, DocumentId documentId, Position position)
=> new VSCompletionItem
private static CompletionItem CreateCompletionItem(XamlCompletionItem xamlCompletion, DocumentId documentId, SourceText text, Position position)
{
TextEdit? textEdit = null;

if (xamlCompletion.Span.HasValue)
{
textEdit = new TextEdit();
textEdit.NewText = xamlCompletion.InsertText;
mgoertz-msft marked this conversation as resolved.
Show resolved Hide resolved
textEdit.Range = ProtocolConversions.LinePositionToRange(text.Lines.GetLinePositionSpan(xamlCompletion.Span.Value));
}

return new VSCompletionItem
{
Label = xamlCompletion.DisplayText,
CommitCharacters = xamlCompletion.CommitCharacters,
Detail = xamlCompletion.Detail,
InsertText = xamlCompletion.InsertText,
TextEdit = textEdit,
Preselect = xamlCompletion.Preselect,
SortText = xamlCompletion.SortText,
FilterText = xamlCompletion.FilterText,
Expand All @@ -65,8 +80,9 @@ private static CompletionItem CreateCompletionItem(IXamlCompletionItem xamlCompl
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)
private static CompletionItem[] CreateErrorItem(string message, string? details = null)
{
var item = new CompletionItem
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public CompletionResolveHandler(ILspSolutionProvider solutionProvider) : base(so

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);
var symbol = await completionService.GetSymbolAsync(new XamlCompletionContext(document, offset), completionItem.Label, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
{
return completionItem;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServer;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Formatting;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
internal abstract class AbstractFormatDocumentHandlerBase<RequestType, ResponseType> : AbstractRequestHandler<RequestType, ResponseType>
{
protected AbstractFormatDocumentHandlerBase(ILspSolutionProvider solutionProvider) : base(solutionProvider)
{
}

protected async Task<LSP.TextEdit[]> GetTextEditsAsync(LSP.TextDocumentIdentifier documentIdentifier, LSP.FormattingOptions formattingOptions, RequestContext context, CancellationToken cancellationToken, LSP.Range? range = null)
{
using var _ = ArrayBuilder<LSP.TextEdit>.GetInstance(out var edits);

var document = SolutionProvider.GetTextDocument(documentIdentifier, context.ClientName);
var formattingService = document?.Project.LanguageServices.GetService<IXamlFormattingService>();

if (document != null && formattingService != null)
{
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
TextSpan? textSpan = null;
if (range != null)
{
textSpan = ProtocolConversions.RangeToTextSpan(range, text);
}

var options = new XamlFormattingOptions { InsertSpaces = formattingOptions.InsertSpaces, TabSize = formattingOptions.TabSize, OtherOptions = formattingOptions.OtherOptions };
var textChanges = await formattingService.GetFormattingChangesAsync(document, options, textSpan, cancellationToken).ConfigureAwait(false);
edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text)));
}

return edits.ToArray();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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 LSP = Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler
{
[Shared]
[ExportLspMethod(LSP.Methods.TextDocumentFormattingName, StringConstants.XamlLanguageName)]
internal class FormatDocumentHandler : AbstractFormatDocumentHandlerBase<LSP.DocumentFormattingParams, LSP.TextEdit[]>
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public FormatDocumentHandler(ILspSolutionProvider solutionProvider) : base(solutionProvider)
{
}

public override Task<LSP.TextEdit[]> HandleRequestAsync(LSP.DocumentFormattingParams request, RequestContext context, CancellationToken cancellationToken)
=> GetTextEditsAsync(request.TextDocument, request.Options, context, cancellationToken);
}
}
Loading