22// Licensed under the MIT license. See License.txt in the project root for license information.
33
44using System ;
5+ using System . Collections . Generic ;
56using System . Collections . Immutable ;
67using System . ComponentModel . Composition ;
78using System . IO ;
89using System . Threading ;
910using System . Threading . Tasks ;
1011using Microsoft . AspNetCore . Razor ;
12+ using Microsoft . AspNetCore . Razor . ProjectSystem ;
1113using Microsoft . AspNetCore . Razor . Utilities ;
1214using Microsoft . CodeAnalysis . Razor ;
1315using Microsoft . CodeAnalysis . Razor . Logging ;
@@ -29,7 +31,8 @@ internal partial class BackgroundDocumentGenerator : IRazorStartupService, IDisp
2931 private readonly ILogger _logger ;
3032
3133 private readonly CancellationTokenSource _disposeTokenSource ;
32- private readonly AsyncBatchingWorkQueue < ( ProjectSnapshot , DocumentSnapshot ) > _workQueue ;
34+ private readonly AsyncBatchingWorkQueue < DocumentKey > _workQueue ;
35+ private readonly HashSet < DocumentKey > _workerSet ;
3336 private ImmutableHashSet < string > _suppressedDocuments ;
3437 private bool _solutionIsClosing ;
3538
@@ -57,8 +60,9 @@ protected BackgroundDocumentGenerator(
5760 _loggerFactory = loggerFactory ;
5861 _logger = loggerFactory . GetOrCreateLogger < BackgroundDocumentGenerator > ( ) ;
5962
63+ _workerSet = [ ] ;
6064 _disposeTokenSource = new ( ) ;
61- _workQueue = new AsyncBatchingWorkQueue < ( ProjectSnapshot , DocumentSnapshot ) > (
65+ _workQueue = new AsyncBatchingWorkQueue < DocumentKey > (
6266 delay ,
6367 processBatchAsync : ProcessBatchAsync ,
6468 equalityComparer : null ,
@@ -82,52 +86,59 @@ public void Dispose()
8286 protected Task WaitUntilCurrentBatchCompletesAsync ( )
8387 => _workQueue . WaitUntilCurrentBatchCompletesAsync ( ) ;
8488
85- protected virtual async Task ProcessDocumentAsync ( ProjectSnapshot project , DocumentSnapshot document , CancellationToken cancellationToken )
89+ protected virtual async Task ProcessDocumentAsync ( DocumentSnapshot document , CancellationToken cancellationToken )
8690 {
8791 await document . GetGeneratedOutputAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
8892
89- UpdateFileInfo ( project , document ) ;
93+ UpdateFileInfo ( document ) ;
9094 }
9195
92- public virtual void Enqueue ( ProjectSnapshot project , DocumentSnapshot document )
96+ public virtual void Enqueue ( DocumentKey documentKey )
9397 {
9498 if ( _disposeTokenSource . IsCancellationRequested )
9599 {
96100 return ;
97101 }
98102
99- if ( _fallbackProjectManager . IsFallbackProject ( project . Key ) )
103+ if ( _fallbackProjectManager . IsFallbackProject ( documentKey . ProjectKey ) )
100104 {
101105 // We don't support closed file code generation for fallback projects
102106 return ;
103107 }
104108
105- if ( Suppressed ( project , document ) )
109+ if ( Suppressed ( documentKey ) )
106110 {
107111 return ;
108112 }
109113
110- _workQueue . AddWork ( ( project , document ) ) ;
114+ _workQueue . AddWork ( documentKey ) ;
111115 }
112116
113- protected virtual async ValueTask ProcessBatchAsync ( ImmutableArray < ( ProjectSnapshot , DocumentSnapshot ) > items , CancellationToken token )
117+ protected virtual async ValueTask ProcessBatchAsync ( ImmutableArray < DocumentKey > items , CancellationToken token )
114118 {
115- foreach ( var ( project , document ) in items . GetMostRecentUniqueItems ( Comparer . Instance ) )
119+ _workerSet . Clear ( ) ;
120+
121+ foreach ( var key in items . GetMostRecentUniqueItems ( _workerSet ) )
116122 {
117123 if ( token . IsCancellationRequested )
118124 {
119125 return ;
120126 }
121127
122- // If the solution is closing, suspect any in-progress work
128+ // If the solution is closing, escape any in-progress work
123129 if ( _solutionIsClosing )
124130 {
125131 break ;
126132 }
127133
134+ if ( ! _projectManager . TryGetDocument ( key , out var document ) )
135+ {
136+ continue ;
137+ }
138+
128139 try
129140 {
130- await ProcessDocumentAsync ( project , document , token ) . ConfigureAwait ( false ) ;
141+ await ProcessDocumentAsync ( document , token ) . ConfigureAwait ( false ) ;
131142 }
132143 catch ( UnauthorizedAccessException )
133144 {
@@ -140,34 +151,34 @@ protected virtual async ValueTask ProcessBatchAsync(ImmutableArray<(ProjectSnaps
140151 }
141152 catch ( Exception ex )
142153 {
143- _logger . LogError ( ex , $ "Error encountered from project '{ project . FilePath } ':{ Environment . NewLine } { ex } ") ;
154+ _logger . LogError ( ex , $ "Error encountered from project '{ document . Project . FilePath } ':{ Environment . NewLine } { ex } ") ;
144155 }
145156 }
146157 }
147158
148- private bool Suppressed ( ProjectSnapshot project , DocumentSnapshot document )
159+ private bool Suppressed ( DocumentKey documentKey )
149160 {
150- var filePath = document . FilePath ;
161+ var filePath = documentKey . FilePath ;
151162
152163 if ( _projectManager . IsDocumentOpen ( filePath ) )
153164 {
154165 ImmutableInterlocked . Update ( ref _suppressedDocuments , static ( set , filePath ) => set . Add ( filePath ) , filePath ) ;
155- _infoProvider . SuppressDocument ( project . Key , filePath ) ;
166+ _infoProvider . SuppressDocument ( documentKey ) ;
156167 return true ;
157168 }
158169
159170 ImmutableInterlocked . Update ( ref _suppressedDocuments , static ( set , filePath ) => set . Remove ( filePath ) , filePath ) ;
160171 return false ;
161172 }
162173
163- private void UpdateFileInfo ( ProjectSnapshot project , DocumentSnapshot document )
174+ private void UpdateFileInfo ( DocumentSnapshot document )
164175 {
165176 var filePath = document . FilePath ;
166177
167178 if ( ! _suppressedDocuments . Contains ( filePath ) )
168179 {
169180 var container = new DefaultDynamicDocumentContainer ( document , _loggerFactory ) ;
170- _infoProvider . UpdateFileInfo ( project . Key , container ) ;
181+ _infoProvider . UpdateFileInfo ( document . Project . Key , container ) ;
171182 }
172183 }
173184
@@ -190,9 +201,9 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
190201
191202 foreach ( var documentFilePath in newProject . DocumentFilePaths )
192203 {
193- if ( newProject . TryGetDocument ( documentFilePath , out var document ) )
204+ if ( newProject . ContainsDocument ( documentFilePath ) )
194205 {
195- Enqueue ( newProject , document ) ;
206+ Enqueue ( new ( newProject . Key , documentFilePath ) ) ;
196207 }
197208 }
198209
@@ -205,9 +216,9 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
205216
206217 foreach ( var documentFilePath in newProject . DocumentFilePaths )
207218 {
208- if ( newProject . TryGetDocument ( documentFilePath , out var document ) )
219+ if ( newProject . ContainsDocument ( documentFilePath ) )
209220 {
210- Enqueue ( newProject , document ) ;
221+ Enqueue ( new ( newProject . Key , documentFilePath ) ) ;
211222 }
212223 }
213224
@@ -222,11 +233,11 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
222233
223234 if ( newProject . TryGetDocument ( documentFilePath , out var document ) )
224235 {
225- Enqueue ( newProject , document ) ;
236+ Enqueue ( document . Key ) ;
226237
227238 foreach ( var relatedDocument in newProject . GetRelatedDocuments ( document ) )
228239 {
229- Enqueue ( newProject , relatedDocument ) ;
240+ Enqueue ( relatedDocument . Key ) ;
230241 }
231242 }
232243
@@ -245,7 +256,7 @@ private void ProjectManager_Changed(object sender, ProjectChangeEventArgs args)
245256 {
246257 foreach ( var relatedDocument in newProject . GetRelatedDocuments ( document ) )
247258 {
248- Enqueue ( newProject , relatedDocument ) ;
259+ Enqueue ( relatedDocument . Key ) ;
249260 }
250261 }
251262
0 commit comments