11// Licensed to the .NET Foundation under one or more agreements.
22// The .NET Foundation licenses this file to you under the MIT license.
33
4+ using Microsoft . AspNetCore . Razor . Language ;
45using System ;
5- using System . Collections . Generic ;
66using System . Diagnostics ;
7- using System . Linq ;
87using System . Threading ;
9- using Microsoft . AspNetCore . Razor . Language ;
108
119namespace Microsoft . NET . Sdk . Razor . SourceGenerators ;
1210
@@ -65,7 +63,11 @@ public SourceGeneratorRazorCodeDocument ProcessInitialParse(RazorProjectItem pro
6563 return new SourceGeneratorRazorCodeDocument ( codeDocument ) ;
6664 }
6765
68- public SourceGeneratorRazorCodeDocument ProcessTagHelpers ( SourceGeneratorRazorCodeDocument sgDocument , TagHelperCollection tagHelpers , bool checkForIdempotency , CancellationToken cancellationToken )
66+ public SourceGeneratorRazorCodeDocument ProcessTagHelpers (
67+ SourceGeneratorRazorCodeDocument sgDocument ,
68+ TagHelperCollection tagHelpers ,
69+ bool checkForIdempotency ,
70+ CancellationToken cancellationToken )
6971 {
7072 Debug . Assert ( sgDocument . CodeDocument . GetPreTagHelperSyntaxTree ( ) is not null ) ;
7173
@@ -88,12 +90,11 @@ public SourceGeneratorRazorCodeDocument ProcessTagHelpers(SourceGeneratorRazorCo
8890 // re-run discovery to figure out which tag helpers are now in scope for this document
8991 codeDocument . SetTagHelpers ( tagHelpers ) ;
9092 _discoveryPhase . Execute ( codeDocument , cancellationToken ) ;
91- var tagHelpersInScope = codeDocument . GetRequiredTagHelperContext ( ) . TagHelpers ;
93+
94+ var newTagHelpersInScope = codeDocument . GetRequiredTagHelperContext ( ) . TagHelpers ;
9295
9396 // Check if any new tag helpers were added or ones we previously used were removed
94- var newVisibleTagHelpers = tagHelpersInScope . Except ( previousTagHelpersInScope ) ;
95- var newUnusedTagHelpers = previousUsedTagHelpers . Except ( tagHelpersInScope ) ;
96- if ( ! newVisibleTagHelpers . Any ( ) && ! newUnusedTagHelpers . Any ( ) )
97+ if ( ! RequiresRewrite ( newTagHelpersInScope , previousTagHelpersInScope , previousUsedTagHelpers ) )
9798 {
9899 // No newly visible tag helpers, and any that got removed weren't used by this document anyway
99100 return sgDocument ;
@@ -112,6 +113,56 @@ public SourceGeneratorRazorCodeDocument ProcessTagHelpers(SourceGeneratorRazorCo
112113 return new SourceGeneratorRazorCodeDocument ( codeDocument ) ;
113114 }
114115
116+ private static bool RequiresRewrite (
117+ TagHelperCollection newTagHelpers ,
118+ TagHelperCollection previousTagHelpers ,
119+ TagHelperCollection previousUsedTagHelpers )
120+ {
121+ // Check if any new tag helpers were added (that weren't in scope before)
122+ // Check if any previously used tag helpers were removed (no longer in scope)
123+ return HasAnyNotIn ( newTagHelpers , previousTagHelpers ) ||
124+ HasAnyNotIn ( previousUsedTagHelpers , newTagHelpers ) ;
125+ }
126+
127+ /// <summary>
128+ /// Determines whether the first collection contains any tag helper descriptors that are not present
129+ /// in the second collection.
130+ /// </summary>
131+ /// <param name="first">The collection to check for unique items.</param>
132+ /// <param name="second">The collection to compare against.</param>
133+ /// <returns>
134+ /// <see langword="true"/> if <paramref name="first"/> contains any descriptors not present in
135+ /// <paramref name="second"/>; otherwise, <see langword="false"/>.
136+ /// </returns>
137+ private static bool HasAnyNotIn ( TagHelperCollection first , TagHelperCollection second )
138+ {
139+ if ( first . IsEmpty )
140+ {
141+ return false ;
142+ }
143+
144+ if ( second . IsEmpty )
145+ {
146+ return true ;
147+ }
148+
149+ if ( first . Equals ( second ) )
150+ {
151+ return false ;
152+ }
153+
154+ // For each item in the first collection, check if it exists in the second collection
155+ foreach ( var item in first )
156+ {
157+ if ( ! second . Contains ( item ) )
158+ {
159+ return true ;
160+ }
161+ }
162+
163+ return false ;
164+ }
165+
115166 public SourceGeneratorRazorCodeDocument ProcessRemaining ( SourceGeneratorRazorCodeDocument sgDocument , CancellationToken cancellationToken )
116167 {
117168 var codeDocument = sgDocument . CodeDocument ;
0 commit comments