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 @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
Expand All @@ -26,6 +27,14 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
}

if (context.ContainsDiagnostic(ComponentDiagnosticFactory.UnexpectedMarkupElement.Id) &&
!context.HasSelection)
{
// If we are telling the user that a component doesn't exist, and they just have their cursor in the tag, they
// won't get any benefit from extracting a non-existing component to a new component.
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
}

if (!FileKinds.IsComponent(context.CodeDocument.FileKind))
{
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor;
Expand All @@ -14,7 +12,6 @@
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using Microsoft.AspNetCore.Razor.Language.Syntax;
using Microsoft.AspNetCore.Razor.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.CodeActions.Models;
using SyntaxFacts = Microsoft.CodeAnalysis.CSharp.SyntaxFacts;

Expand All @@ -26,8 +23,7 @@ internal class GenerateMethodCodeActionProvider : IRazorCodeActionProvider
{
public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeActionContext context, CancellationToken cancellationToken)
{
var nameNotExistDiagnostics = context.Request.Context.Diagnostics.Any(d => d.Code == "CS0103");
if (!nameNotExistDiagnostics)
if (!context.ContainsDiagnostic("CS0103"))
{
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,6 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
}

var owner = syntaxTree.Root.FindNode(TextSpan.FromBounds(context.StartAbsoluteIndex, context.EndAbsoluteIndex));
if (owner is null)
{
return SpecializedTasks.EmptyImmutableArray<RazorVSInternalCodeAction>();
}

var attributes = FindAttributes(owner);
if (attributes.Count == 0)
{
Expand Down Expand Up @@ -100,8 +95,24 @@ public Task<ImmutableArray<RazorVSInternalCodeAction>> ProvideAsync(RazorCodeAct
return Task.FromResult<ImmutableArray<RazorVSInternalCodeAction>>([action]);
}

private AspNetCore.Razor.Language.Syntax.SyntaxList<RazorSyntaxNode> FindAttributes(AspNetCore.Razor.Language.Syntax.SyntaxNode owner)
private static AspNetCore.Razor.Language.Syntax.SyntaxList<RazorSyntaxNode> FindAttributes(AspNetCore.Razor.Language.Syntax.SyntaxNode? owner)
{
// Sometimes FindNode will find the start tag, sometimes the element. We always start from the start tag to make searching
// easier, and since we are concerned with attributes, things without start tags wouldn't be applicalbe anyway
if (owner is MarkupElementSyntax element)
{
owner = element.StartTag;
}
else if (owner is MarkupTagHelperElementSyntax tagHelperElement)
{
owner = tagHelperElement.StartTag;
}

if (owner is null)
{
return [];
}

foreach (var node in owner.AncestorsAndSelf())
{
if (node is MarkupStartTagSyntax startTag)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,24 @@ internal sealed record class RazorCodeActionContext(
bool SupportsCodeActionResolve)
{
public bool HasSelection => StartAbsoluteIndex != EndAbsoluteIndex;

public bool ContainsDiagnostic(string code)
{
if (Request.Context.Diagnostics is null)
{
return false;
}

foreach (var diagnostic in Request.Context.Diagnostics)
{
if (diagnostic.Code is { } codeSumType &&
codeSumType.TryGetSecond(out var codeString) &&
codeString == code)
{
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,14 @@ private protected async Task VerifyCodeActionAsync(TestCode input, string? expec
}
}

var range = input.HasSpans
? inputText.GetRange(input.Span)
: inputText.GetRange(input.Position, input.Position);

var request = new VSCodeActionParams
{
TextDocument = new VSTextDocumentIdentifier { Uri = document.CreateUri() },
Range = inputText.GetRange(input.Span),
Range = range,
Context = new VSInternalCodeActionContext() { Diagnostics = diagnostics.ToArray() }
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,23 @@ Hello World
</div>
""")]);
}

[Fact]
public async Task DontOfferOnNonExistentComponent()
{
await VerifyCodeActionAsync(
input: """
<div></div>

<div>
Hello World
</div>

<{|RZ10012:Not$$AComponent|} />

<div></div>
""",
expected: null,
codeActionName: WorkspacesSR.ExtractTo_Component_Title);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,31 @@ await VerifyCodeActionAsync(
expected: null,
codeActionName: LanguageServerConstants.CodeActions.WrapAttributes);
}

[Fact]
public async Task SelfClosing()
{
await VerifyCodeActionAsync(
input: """
@if (true)
{
<div>
<in[||]put bar="Baz" Zip="Zap" checked @onclick="foo" Pop="Pap" />
</div>
}
""",
expected: """
@if (true)
{
<div>
<input bar="Baz"
Zip="Zap"
checked
@onclick="foo"
Pop="Pap" />
</div>
}
""",
codeActionName: LanguageServerConstants.CodeActions.WrapAttributes);
}
}