Skip to content
Merged
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
@@ -1,12 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Razor.Language;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.AspNetCore.Razor.Language;

namespace Microsoft.NET.Sdk.Razor.SourceGenerators;

Expand Down Expand Up @@ -65,7 +63,11 @@ public SourceGeneratorRazorCodeDocument ProcessInitialParse(RazorProjectItem pro
return new SourceGeneratorRazorCodeDocument(codeDocument);
}

public SourceGeneratorRazorCodeDocument ProcessTagHelpers(SourceGeneratorRazorCodeDocument sgDocument, TagHelperCollection tagHelpers, bool checkForIdempotency, CancellationToken cancellationToken)
public SourceGeneratorRazorCodeDocument ProcessTagHelpers(
SourceGeneratorRazorCodeDocument sgDocument,
TagHelperCollection tagHelpers,
bool checkForIdempotency,
CancellationToken cancellationToken)
{
Debug.Assert(sgDocument.CodeDocument.GetPreTagHelperSyntaxTree() is not null);

Expand All @@ -88,12 +90,11 @@ public SourceGeneratorRazorCodeDocument ProcessTagHelpers(SourceGeneratorRazorCo
// re-run discovery to figure out which tag helpers are now in scope for this document
codeDocument.SetTagHelpers(tagHelpers);
_discoveryPhase.Execute(codeDocument, cancellationToken);
var tagHelpersInScope = codeDocument.GetRequiredTagHelperContext().TagHelpers;

var newTagHelpersInScope = codeDocument.GetRequiredTagHelperContext().TagHelpers;

// Check if any new tag helpers were added or ones we previously used were removed
var newVisibleTagHelpers = tagHelpersInScope.Except(previousTagHelpersInScope);
var newUnusedTagHelpers = previousUsedTagHelpers.Except(tagHelpersInScope);
if (!newVisibleTagHelpers.Any() && !newUnusedTagHelpers.Any())
if (!RequiresRewrite(newTagHelpersInScope, previousTagHelpersInScope, previousUsedTagHelpers))
{
// No newly visible tag helpers, and any that got removed weren't used by this document anyway
return sgDocument;
Expand All @@ -112,6 +113,56 @@ public SourceGeneratorRazorCodeDocument ProcessTagHelpers(SourceGeneratorRazorCo
return new SourceGeneratorRazorCodeDocument(codeDocument);
}

private static bool RequiresRewrite(
TagHelperCollection newTagHelpers,
TagHelperCollection previousTagHelpers,
TagHelperCollection previousUsedTagHelpers)
{
// Check if any new tag helpers were added (that weren't in scope before)
// Check if any previously used tag helpers were removed (no longer in scope)
return HasAnyNotIn(newTagHelpers, previousTagHelpers) ||
HasAnyNotIn(previousUsedTagHelpers, newTagHelpers);
}

/// <summary>
/// Determines whether the first collection contains any tag helper descriptors that are not present
/// in the second collection.
/// </summary>
/// <param name="first">The collection to check for unique items.</param>
/// <param name="second">The collection to compare against.</param>
/// <returns>
/// <see langword="true"/> if <paramref name="first"/> contains any descriptors not present in
/// <paramref name="second"/>; otherwise, <see langword="false"/>.
/// </returns>
private static bool HasAnyNotIn(TagHelperCollection first, TagHelperCollection second)
{
if (first.IsEmpty)
{
return false;
}

if (second.IsEmpty)
{
return true;
}

if (first.Equals(second))
{
return false;
}

// For each item in the first collection, check if it exists in the second collection
foreach (var item in first)
{
if (!second.Contains(item))
{
return true;
}
}

return false;
}

public SourceGeneratorRazorCodeDocument ProcessRemaining(SourceGeneratorRazorCodeDocument sgDocument, CancellationToken cancellationToken)
{
var codeDocument = sgDocument.CodeDocument;
Expand Down