@@ -15,7 +15,6 @@ internal sealed class CompilationHandler : IDisposable
1515 {
1616 public readonly IncrementalMSBuildWorkspace Workspace ;
1717 public readonly EnvironmentOptions EnvironmentOptions ;
18- private readonly GlobalOptions _options ;
1918 private readonly IReporter _reporter ;
2019 private readonly WatchHotReloadService _hotReloadService ;
2120
@@ -41,11 +40,10 @@ internal sealed class CompilationHandler : IDisposable
4140
4241 private bool _isDisposed ;
4342
44- public CompilationHandler ( IReporter reporter , EnvironmentOptions environmentOptions , GlobalOptions options , CancellationToken shutdownCancellationToken )
43+ public CompilationHandler ( IReporter reporter , EnvironmentOptions environmentOptions , CancellationToken shutdownCancellationToken )
4544 {
4645 _reporter = reporter ;
4746 EnvironmentOptions = environmentOptions ;
48- _options = options ;
4947 Workspace = new IncrementalMSBuildWorkspace ( reporter ) ;
5048 _hotReloadService = new WatchHotReloadService ( Workspace . CurrentSolution . Services , ( ) => ValueTask . FromResult ( GetAggregateCapabilities ( ) ) ) ;
5149 _shutdownCancellationToken = shutdownCancellationToken ;
@@ -265,6 +263,7 @@ private static void PrepareCompilations(Solution solution, string projectPath, C
265263 }
266264
267265 public async ValueTask < ( ImmutableDictionary < ProjectId , string > projectsToRebuild , ImmutableArray < RunningProject > terminatedProjects ) > HandleManagedCodeChangesAsync (
266+ bool autoRestart ,
268267 Func < IEnumerable < string > , CancellationToken , Task > restartPrompt ,
269268 CancellationToken cancellationToken )
270269 {
@@ -275,8 +274,8 @@ private static void PrepareCompilations(Solution solution, string projectPath, C
275274 ( from project in currentSolution . Projects
276275 let runningProject = GetCorrespondingRunningProject ( project , runningProjects )
277276 where runningProject != null
278- let autoRestart = _options . NonInteractive || runningProject . ProjectNode . IsAutoRestartEnabled ( )
279- select ( project . Id , info : new WatchHotReloadService . RunningProjectInfo ( ) { RestartWhenChangesHaveNoEffect = autoRestart } ) )
277+ let autoRestartProject = autoRestart || runningProject . ProjectNode . IsAutoRestartEnabled ( )
278+ select ( project . Id , info : new WatchHotReloadService . RunningProjectInfo ( ) { RestartWhenChangesHaveNoEffect = autoRestartProject } ) )
280279 . ToImmutableDictionary ( e => e . Id , e => e . info ) ;
281280
282281 var updates = await _hotReloadService . GetUpdatesAsync ( currentSolution , runningProjectInfos , cancellationToken ) ;
@@ -290,41 +289,43 @@ private static void PrepareCompilations(Solution solution, string projectPath, C
290289 return ( ImmutableDictionary < ProjectId , string > . Empty , [ ] ) ;
291290 }
292291
293- ImmutableDictionary < string , ImmutableArray < RunningProject > > projectsToUpdate ;
294- lock ( _runningProjectsAndUpdatesGuard )
292+ if ( updates . ProjectUpdates . Any ( ) )
295293 {
296- // Adding the updates makes sure that all new processes receive them before they are added to running processes.
297- _previousUpdates = _previousUpdates . AddRange ( updates . ProjectUpdates ) ;
294+ ImmutableDictionary < string , ImmutableArray < RunningProject > > projectsToUpdate ;
295+ lock ( _runningProjectsAndUpdatesGuard )
296+ {
297+ // Adding the updates makes sure that all new processes receive them before they are added to running processes.
298+ _previousUpdates = _previousUpdates . AddRange ( updates . ProjectUpdates ) ;
298299
299- // Capture the set of processes that do not have the currently calculated deltas yet.
300- projectsToUpdate = _runningProjects ;
301- }
300+ // Capture the set of processes that do not have the currently calculated deltas yet.
301+ projectsToUpdate = _runningProjects ;
302+ }
302303
303- // Apply changes to all running projects, even if they do not have a static project dependency on any project that changed.
304- // The process may load any of the binaries using MEF or some other runtime dependency loader.
304+ // Apply changes to all running projects, even if they do not have a static project dependency on any project that changed.
305+ // The process may load any of the binaries using MEF or some other runtime dependency loader.
305306
306- await ForEachProjectAsync ( projectsToUpdate , async ( runningProject , cancellationToken ) =>
307- {
308- try
307+ await ForEachProjectAsync ( projectsToUpdate , async ( runningProject , cancellationToken ) =>
309308 {
310- using var processCommunicationCancellationSource = CancellationTokenSource . CreateLinkedTokenSource ( runningProject . ProcessExitedSource . Token , cancellationToken ) ;
311- var applySucceded = await runningProject . DeltaApplier . ApplyManagedCodeUpdates ( updates . ProjectUpdates , processCommunicationCancellationSource . Token ) != ApplyStatus . Failed ;
312- if ( applySucceded )
309+ try
313310 {
314- runningProject . Reporter . Report ( MessageDescriptor . HotReloadSucceeded ) ;
315- if ( runningProject . BrowserRefreshServer is { } server )
311+ using var processCommunicationCancellationSource = CancellationTokenSource . CreateLinkedTokenSource ( runningProject . ProcessExitedSource . Token , cancellationToken ) ;
312+ var applySucceded = await runningProject . DeltaApplier . ApplyManagedCodeUpdates ( updates . ProjectUpdates , processCommunicationCancellationSource . Token ) != ApplyStatus . Failed ;
313+ if ( applySucceded )
316314 {
317- runningProject . Reporter . Verbose ( "Refreshing browser." ) ;
318- await server . RefreshBrowserAsync ( cancellationToken ) ;
315+ runningProject . Reporter . Report ( MessageDescriptor . HotReloadSucceeded ) ;
316+ if ( runningProject . BrowserRefreshServer is { } server )
317+ {
318+ runningProject . Reporter . Verbose ( "Refreshing browser." ) ;
319+ await server . RefreshBrowserAsync ( cancellationToken ) ;
320+ }
319321 }
320322 }
321- }
322- catch ( OperationCanceledException ) when ( runningProject . ProcessExitedSource . Token . IsCancellationRequested && ! cancellationToken . IsCancellationRequested )
323- {
324- runningProject . Reporter . Verbose ( "Hot reload canceled because the process exited." , emoji : "🔥" ) ;
325- }
326- } , cancellationToken ) ;
327-
323+ catch ( OperationCanceledException ) when ( runningProject . ProcessExitedSource . Token . IsCancellationRequested && ! cancellationToken . IsCancellationRequested )
324+ {
325+ runningProject . Reporter . Verbose ( "Hot reload canceled because the process exited." , emoji : "🔥" ) ;
326+ }
327+ } , cancellationToken ) ;
328+ }
328329
329330 if ( updates . ProjectsToRestart . IsEmpty )
330331 {
@@ -430,11 +431,15 @@ void ReportRudeEdits()
430431 // Rude edits in projects that caused restart of a project that can be restarted automatically
431432 // will be reported only as verbose output.
432433 var projectsRestartedDueToRudeEdits = updates . ProjectsToRestart
433- . Where ( e => runningProjectInfos . TryGetValue ( e . Key , out var info ) && info . RestartWhenChangesHaveNoEffect )
434+ . Where ( e => IsAutoRestartEnabled ( e . Key ) )
434435 . SelectMany ( e => e . Value )
435436 . ToHashSet ( ) ;
436437
437- var projectsRebuiltDueToRudeEdits = updates . ProjectsToRebuild . ToHashSet ( ) ;
438+ // Project with rude edit that doesn't impact running project is only listed in ProjectsToRebuild.
439+ // Such projects are always auto-rebuilt whether or not there is any project to be restarted that needs a confirmation.
440+ var projectsRebuiltDueToRudeEdits = updates . ProjectsToRebuild
441+ . Where ( p => ! updates . ProjectsToRestart . ContainsKey ( p ) )
442+ . ToHashSet ( ) ;
438443
439444 foreach ( var ( projectId , diagnostics ) in updates . RudeEdits )
440445 {
@@ -451,6 +456,9 @@ void ReportRudeEdits()
451456 }
452457 }
453458
459+ bool IsAutoRestartEnabled ( ProjectId id )
460+ => runningProjectInfos . TryGetValue ( id , out var info ) && info . RestartWhenChangesHaveNoEffect ;
461+
454462 void ReportDiagnostic ( Diagnostic diagnostic , MessageDescriptor descriptor , string prefix = "" )
455463 {
456464 var display = CSharpDiagnosticFormatter . Instance . Format ( diagnostic ) ;
0 commit comments