Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -67,11 +67,12 @@ public void ComponentElement_CompletionList_Deserialization()
private CompletionList GenerateCompletionList(string documentContent, int queryIndex, TagHelperCompletionProvider componentCompletionProvider)
{
var sourceDocument = RazorSourceDocument.Create(documentContent, RazorSourceDocumentProperties.Default);
var codeDocument = RazorCodeDocument.Create(sourceDocument);
var syntaxTree = RazorSyntaxTree.Parse(sourceDocument);
var tagHelperDocumentContext = TagHelperDocumentContext.Create(prefix: string.Empty, CommonResources.LegacyTagHelpers);

var owner = syntaxTree.Root.FindInnermostNode(queryIndex, includeWhitespace: true, walkMarkersBack: true);
var context = new RazorCompletionContext(queryIndex, owner, syntaxTree, tagHelperDocumentContext);
var context = new RazorCompletionContext(codeDocument, queryIndex, owner, syntaxTree, tagHelperDocumentContext);

var razorCompletionItems = componentCompletionProvider.GetCompletionItems(context);
var completionList = RazorCompletionListProvider.CreateLSPCompletionList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Microsoft.CodeAnalysis.Razor.Completion;

internal record RazorCompletionContext(
RazorCodeDocument CodeDocument,
int AbsoluteIndex,
RazorSyntaxNode? Owner,
RazorSyntaxTree SyntaxTree,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal sealed class RazorCompletionItem
public object DescriptionInfo { get; }
public ImmutableArray<RazorCommitCharacter> CommitCharacters { get; }
public bool IsSnippet { get; }
public TextEdit[]? AdditionalTextEdits { get; }

/// <summary>
/// Creates a new Razor completion item
Expand All @@ -33,6 +34,7 @@ internal sealed class RazorCompletionItem
/// <param name="descriptionInfo">An object that provides description information for this completion item.</param>
/// <param name="commitCharacters">Characters that can be used to commit the completion item.</param>
/// <param name="isSnippet">Indicates whether the completion item's <see cref="InsertText"/> is an LSP snippet or not.</param>
/// <param name="additionalTextEdits">Additional text edits to apply when the completion is committed.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="displayText"/> or <paramref name="insertText"/> are <see langword="null"/>.</exception>
private RazorCompletionItem(
RazorCompletionItemKind kind,
Expand All @@ -41,7 +43,8 @@ private RazorCompletionItem(
string? sortText,
object descriptionInfo,
ImmutableArray<RazorCommitCharacter> commitCharacters,
bool isSnippet)
bool isSnippet,
TextEdit[]? additionalTextEdits = null)
{
ArgHelper.ThrowIfNull(displayText);
ArgHelper.ThrowIfNull(insertText);
Expand All @@ -53,6 +56,7 @@ private RazorCompletionItem(
DescriptionInfo = descriptionInfo;
CommitCharacters = commitCharacters.NullToEmpty();
IsSnippet = isSnippet;
AdditionalTextEdits = additionalTextEdits;
}

public static RazorCompletionItem CreateDirective(
Expand Down Expand Up @@ -82,8 +86,9 @@ public static RazorCompletionItem CreateMarkupTransition(
public static RazorCompletionItem CreateTagHelperElement(
string displayText, string insertText,
AggregateBoundElementDescription descriptionInfo,
ImmutableArray<RazorCommitCharacter> commitCharacters)
=> new(RazorCompletionItemKind.TagHelperElement, displayText, insertText, sortText: null, descriptionInfo, commitCharacters, isSnippet: false);
ImmutableArray<RazorCommitCharacter> commitCharacters,
TextEdit[]? additionalTextEdits = null)
=> new(RazorCompletionItemKind.TagHelperElement, displayText, insertText, sortText: null, descriptionInfo, commitCharacters, isSnippet: false, additionalTextEdits);

public static RazorCompletionItem CreateTagHelperAttribute(
string displayText, string insertText, string? sortText,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ internal class RazorCompletionListProvider(
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, absoluteIndex);

var razorCompletionContext = new RazorCompletionContext(
codeDocument,
absoluteIndex,
owner,
syntaxTree,
Expand Down Expand Up @@ -136,6 +137,7 @@ internal static bool TryConvert(
SortText = razorCompletionItem.SortText,
InsertTextFormat = insertTextFormat,
Kind = razorCompletionItem.IsSnippet ? CompletionItemKind.Snippet : CompletionItemKind.Keyword,
AdditionalTextEdits = razorCompletionItem.AdditionalTextEdits,
};

directiveCompletionItem.UseCommitCharactersFrom(razorCompletionItem, clientCapabilities);
Expand All @@ -159,6 +161,7 @@ internal static bool TryConvert(
SortText = razorCompletionItem.SortText,
InsertTextFormat = insertTextFormat,
Kind = tagHelperCompletionItemKind,
AdditionalTextEdits = razorCompletionItem.AdditionalTextEdits,
};

directiveAttributeCompletionItem.UseCommitCharactersFrom(razorCompletionItem, clientCapabilities);
Expand All @@ -176,6 +179,7 @@ internal static bool TryConvert(
SortText = razorCompletionItem.SortText,
InsertTextFormat = insertTextFormat,
Kind = tagHelperCompletionItemKind,
AdditionalTextEdits = razorCompletionItem.AdditionalTextEdits,
};

parameterCompletionItem.UseCommitCharactersFrom(razorCompletionItem, clientCapabilities);
Expand All @@ -193,6 +197,7 @@ internal static bool TryConvert(
SortText = razorCompletionItem.SortText,
InsertTextFormat = insertTextFormat,
Kind = CompletionItemKind.Event,
AdditionalTextEdits = razorCompletionItem.AdditionalTextEdits,
};

eventValueCompletionItem.UseCommitCharactersFrom(razorCompletionItem, clientCapabilities);
Expand All @@ -210,6 +215,7 @@ internal static bool TryConvert(
SortText = razorCompletionItem.SortText,
InsertTextFormat = insertTextFormat,
Kind = tagHelperCompletionItemKind,
AdditionalTextEdits = razorCompletionItem.AdditionalTextEdits,
};

markupTransitionCompletionItem.UseCommitCharactersFrom(razorCompletionItem, clientCapabilities);
Expand All @@ -227,6 +233,7 @@ internal static bool TryConvert(
SortText = razorCompletionItem.SortText,
InsertTextFormat = insertTextFormat,
Kind = tagHelperCompletionItemKind,
AdditionalTextEdits = razorCompletionItem.AdditionalTextEdits,
};

tagHelperElementCompletionItem.UseCommitCharactersFrom(razorCompletionItem, clientCapabilities);
Expand All @@ -244,6 +251,7 @@ internal static bool TryConvert(
SortText = razorCompletionItem.SortText,
InsertTextFormat = insertTextFormat,
Kind = tagHelperCompletionItemKind,
AdditionalTextEdits = razorCompletionItem.AdditionalTextEdits,
};

tagHelperAttributeCompletionItem.UseCommitCharactersFrom(razorCompletionItem, clientCapabilities);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor.Formatting;
using Microsoft.CodeAnalysis.Razor.Tooltip;
using Microsoft.VisualStudio.Editor.Razor;
using RazorSyntaxNode = Microsoft.AspNetCore.Razor.Language.Syntax.SyntaxNode;
Expand Down Expand Up @@ -233,20 +234,48 @@ private ImmutableArray<RazorCompletionItem> GetElementCompletions(

foreach (var (displayText, tagHelpers) in completionResult.Completions)
{
var tagHelperDescriptions = tagHelpers.SelectAsArray(BoundElementDescriptionInfo.From);
var descriptionInfo = new AggregateBoundElementDescription(tagHelpers.SelectAsArray(BoundElementDescriptionInfo.From));

var razorCompletionItem = RazorCompletionItem.CreateTagHelperElement(
displayText: displayText,
insertText: displayText,
descriptionInfo: new(tagHelperDescriptions),
descriptionInfo,
commitCharacters: commitChars);

completionItems.Add(razorCompletionItem);
AddCompletionItemWithUsingDirective(ref completionItems.AsRef(), context, commitChars, displayText, descriptionInfo);
}

return completionItems.ToImmutableAndClear();
}

private static void AddCompletionItemWithUsingDirective(ref PooledArrayBuilder<RazorCompletionItem> completionItems, RazorCompletionContext context, ImmutableArray<RazorCommitCharacter> commitChars, string displayText, AggregateBoundElementDescription descriptionInfo)
{
// If this is a fully qualified name (contains a dot), it means there's an out-of-scope component
// so we add an additional completion item with @using hint and additional edits that will insert
// the @using correctly.
var lastDotIndex = displayText.LastIndexOf('.');
if (lastDotIndex == -1)
{
return;
}

var @namespace = displayText[..lastDotIndex];
var shortName = displayText[(lastDotIndex + 1)..]; // Get the short name after the last dot
var displayTextWithUsing = $"{shortName} - @using {@namespace}";

var addUsingEdit = AddUsingsHelper.CreateAddUsingTextEdit(@namespace, context.CodeDocument);

var razorCompletionItemWithUsing = RazorCompletionItem.CreateTagHelperElement(
displayText: displayTextWithUsing,
insertText: shortName,
descriptionInfo,
commitCharacters: commitChars,
additionalTextEdits: [addUsingEdit]);

completionItems.Add(razorCompletionItemWithUsing);
}

private const string BooleanTypeString = "System.Boolean";

private static AttributeContext ResolveAttributeContext(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public async Task<CompletionContext> GetCompletionContextAsync(IAsyncCompletionS
#pragma warning disable CS0618 // Type or member is obsolete, will be removed in an upcoming change
var owner = syntaxTree.Root.LocateOwner(queryableChange);
#pragma warning restore CS0618 // Type or member is obsolete
var razorCompletionContext = new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperContext);
var razorCompletionContext = new RazorCompletionContext(codeDocument, absoluteIndex, owner, syntaxTree, tagHelperContext);
var razorCompletionItems = _completionFactsService.GetCompletionItems(razorCompletionContext);

if (razorCompletionItems.Length == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public async Task<CompletionContext> GetCompletionContextAsync(
#pragma warning disable CS0618 // Type or member is obsolete, will be removed in an upcoming change
var owner = syntaxTree.Root.LocateOwner(queryableChange);
#pragma warning restore CS0618 // Type or member is obsolete
var razorCompletionContext = new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperContext);
var razorCompletionContext = new RazorCompletionContext(codeDocument, absoluteIndex, owner, syntaxTree, tagHelperContext);
var razorCompletionItems = _completionFactsService.GetCompletionItems(razorCompletionContext);

using var _ = ArrayBuilderPool<CompletionItem>.GetPooledObject(out var completionItems);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,6 @@ private static RazorCompletionContext CreateRazorCompletionContext(string markup

var owner = syntaxTree.Root.FindInnermostNode(position, includeWhitespace: true, walkMarkersBack: true);
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, position);
return new RazorCompletionContext(position, owner, syntaxTree, context, Options: options);
return new RazorCompletionContext(codeDocument, position, owner, syntaxTree, context, Options: options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class DefaultRazorCompletionFactsServiceTest(ITestOutputHelper testOutput
public void GetDirectiveCompletionItems_AllProvidersCompletionItems()
{
// Arrange
var sourceDocument = RazorSourceDocument.Create("", RazorSourceDocumentProperties.Default);
var codeDocument = RazorCodeDocument.Create(sourceDocument);
var syntaxTree = RazorSyntaxTree.Parse(TestRazorSourceDocument.Create());
var tagHelperDocumentContext = TagHelperDocumentContext.Create(prefix: null, tagHelpers: []);

Expand All @@ -26,7 +28,7 @@ public void GetDirectiveCompletionItems_AllProvidersCompletionItems()
commitCharacters: [],
isSnippet: false);

var context = new RazorCompletionContext(AbsoluteIndex: 0, Owner: null, syntaxTree, tagHelperDocumentContext);
var context = new RazorCompletionContext(codeDocument, AbsoluteIndex: 0, Owner: null, SyntaxTree: syntaxTree, TagHelperDocumentContext: tagHelperDocumentContext);
var provider1 = StrictMock.Of<IRazorCompletionItemProvider>(p =>
p.GetCompletionItems(context) == ImmutableArray.Create(completionItem1));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ private RazorCompletionContext CreateRazorCompletionContext(TestCode testCode)
var owner = syntaxTree.Root.FindInnermostNode(testCode.Position, includeWhitespace: true, walkMarkersBack: true);
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, testCode.Position);

return new RazorCompletionContext(testCode.Position, owner, syntaxTree, tagHelperContext);
return new RazorCompletionContext(codeDocument, testCode.Position, owner, syntaxTree, tagHelperContext);
}

private RazorSyntaxNode GetOwner(string testCodeText)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,6 @@ private RazorCompletionContext CreateRazorCompletionContext(TestCode documentCon

var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex);
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, absoluteIndex);
return new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperDocumentContext);
return new RazorCompletionContext(codeDocument, absoluteIndex, owner, syntaxTree, tagHelperDocumentContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,6 @@ private RazorCompletionContext CreateRazorCompletionContext(int absoluteIndex, s

var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex);
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, absoluteIndex);
return new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperContext);
return new RazorCompletionContext(codeDocument, absoluteIndex, owner, syntaxTree, tagHelperContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ public void GetCompletionItems_WithAvoidExplicitCommitOption_ReturnsAppropriateC
}
}

private static RazorSyntaxTree GetSyntaxTree(string text, RazorFileKind? fileKind = null)
private static RazorCodeDocument GetCodeDocument(string text, RazorFileKind? fileKind = null)
{
var fileKindValue = fileKind ?? RazorFileKind.Component;

Expand All @@ -340,17 +340,16 @@ private static RazorSyntaxTree GetSyntaxTree(string text, RazorFileKind? fileKin
});
});

var codeDocument = projectEngine.Process(sourceDocument, fileKindValue, importSources: default, tagHelpers: []);

return codeDocument.GetRequiredSyntaxTree();
return projectEngine.Process(sourceDocument, fileKindValue, importSources: default, tagHelpers: []);
}

private RazorCompletionContext CreateContext(int absoluteIndex, string documentContent, RazorFileKind? fileKind = null)
{
var syntaxTree = GetSyntaxTree(documentContent, fileKind);
var codeDocument = GetCodeDocument(documentContent, fileKind);
var syntaxTree = codeDocument.GetRequiredSyntaxTree();
var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex, includeWhitespace: true, walkMarkersBack: true);
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, absoluteIndex);

return new RazorCompletionContext(absoluteIndex, owner, syntaxTree, _tagHelperDocumentContext);
return new RazorCompletionContext(codeDocument, absoluteIndex, owner, syntaxTree, _tagHelperDocumentContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -430,10 +430,13 @@ public void IsDirectiveCompletableToken_ReturnsFalseForInvalidCSharpTokens()

private static RazorCompletionContext CreateRazorCompletionContext(int absoluteIndex, RazorSyntaxTree syntaxTree, CompletionReason reason = CompletionReason.Invoked)
{
var sourceDocument = RazorSourceDocument.Create("", RazorSourceDocumentProperties.Default);
var codeDocument = RazorCodeDocument.Create(sourceDocument);

var tagHelperDocumentContext = TagHelperDocumentContext.Create(prefix: string.Empty, tagHelpers: []);
var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex);
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, absoluteIndex);
return new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperDocumentContext, reason);
return new RazorCompletionContext(codeDocument, absoluteIndex, owner, syntaxTree, tagHelperDocumentContext, reason);
}

private static void AssertRazorCompletionItem(string completionDisplayText, DirectiveDescriptor directive, RazorCompletionItem item, ImmutableArray<RazorCommitCharacter> commitCharacters = default, bool isSnippet = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,11 +326,14 @@ private static void AssertRazorCompletionItem(RazorCompletionItem item)

private static RazorCompletionContext CreateRazorCompletionContext(int absoluteIndex, RazorSyntaxTree syntaxTree)
{
var sourceDocument = RazorSourceDocument.Create("", RazorSourceDocumentProperties.Default);
var codeDocument = RazorCodeDocument.Create(sourceDocument);

var tagHelperDocumentContext = TagHelperDocumentContext.Create(prefix: string.Empty, tagHelpers: []);

var owner = syntaxTree.Root.FindInnermostNode(absoluteIndex, includeWhitespace: true, walkMarkersBack: true);
owner = AbstractRazorCompletionFactsService.AdjustSyntaxNodeForWordBoundary(owner, absoluteIndex);
return new RazorCompletionContext(absoluteIndex, owner, syntaxTree, tagHelperDocumentContext);
return new RazorCompletionContext(codeDocument, absoluteIndex, owner, syntaxTree, tagHelperDocumentContext);
}

private static RazorSyntaxTree CreateSyntaxTree(string text, params DirectiveDescriptor[] directives)
Expand Down
Loading