@@ -15,7 +15,7 @@ namespace Microsoft.DotNet.Watch
1515{
1616 internal sealed class CompilationHandler : IDisposable
1717 {
18- public readonly IncrementalMSBuildWorkspace Workspace ;
18+ public readonly HotReloadMSBuildWorkspace Workspace ;
1919 private readonly DotNetWatchContext _context ;
2020 private readonly HotReloadService _hotReloadService ;
2121
@@ -38,11 +38,19 @@ internal sealed class CompilationHandler : IDisposable
3838 private ImmutableList < HotReloadService . Update > _previousUpdates = [ ] ;
3939
4040 private bool _isDisposed ;
41+ private int _solutionUpdateId ;
42+
43+ /// <summary>
44+ /// Current set of project instances indexed by <see cref="ProjectInstance.FullPath"/>.
45+ /// Updated whenever the project graph changes.
46+ /// </summary>
47+ private ImmutableDictionary < string , ImmutableArray < ProjectInstance > > _projectInstances = [ ] ;
4148
4249 public CompilationHandler ( DotNetWatchContext context )
4350 {
4451 _context = context ;
45- Workspace = new IncrementalMSBuildWorkspace ( context . Logger ) ;
52+ _processRunner = processRunner ;
53+ Workspace = new HotReloadMSBuildWorkspace ( logger , projectFile => ( instances : _projectInstances . GetValueOrDefault ( projectFile , [ ] ) , project : null ) ) ;
4654 _hotReloadService = new HotReloadService ( Workspace . CurrentSolution . Services , ( ) => ValueTask . FromResult ( GetAggregateCapabilities ( ) ) ) ;
4755 }
4856
@@ -798,5 +806,70 @@ private static Task ForEachProjectAsync(ImmutableDictionary<string, ImmutableArr
798806
799807 private static ImmutableArray < HotReloadManagedCodeUpdate > ToManagedCodeUpdates ( ImmutableArray < HotReloadService . Update > updates )
800808 => [ .. updates . Select ( update => new HotReloadManagedCodeUpdate ( update . ModuleId , update . MetadataDelta , update . ILDelta , update . PdbDelta , update . UpdatedTypes , update . RequiredCapabilities ) ) ] ;
809+
810+ private static ImmutableDictionary < string , ImmutableArray < ProjectInstance > > CreateProjectInstanceMap ( ProjectGraph graph )
811+ => graph . ProjectNodes
812+ . GroupBy ( static node => node . ProjectInstance . FullPath )
813+ . ToImmutableDictionary (
814+ keySelector : static group => group . Key ,
815+ elementSelector : static group => group . Select ( static node => node . ProjectInstance ) . ToImmutableArray ( ) ) ;
816+
817+ public async Task UpdateProjectConeAsync ( ProjectGraph projectGraph , string projectPath , CancellationToken cancellationToken )
818+ {
819+ _logger . LogInformation ( "Loading projects ..." ) ;
820+ var stopwatch = Stopwatch . StartNew ( ) ;
821+
822+ _projectInstances = CreateProjectInstanceMap ( projectGraph ) ;
823+
824+ var solution = await Workspace . UpdateProjectConeAsync ( projectPath , cancellationToken ) ;
825+ await SolutionUpdatedAsync ( solution , "project update" , cancellationToken ) ;
826+
827+ _logger . LogInformation ( "Projects loaded in {Time}s." , stopwatch . Elapsed . TotalSeconds . ToString ( "0.0" ) ) ;
828+ }
829+
830+ public async Task UpdateFileContentAsync ( ImmutableList < ChangedFile > changedFiles , CancellationToken cancellationToken )
831+ {
832+ var solution = await Workspace . UpdateFileContentAsync ( changedFiles . Select ( static f => ( f . Item . FilePath , f . Kind . Convert ( ) ) ) , cancellationToken ) ;
833+ await SolutionUpdatedAsync ( solution , "document update" , cancellationToken ) ;
834+ }
835+
836+ private Task SolutionUpdatedAsync ( Solution newSolution , string operationDisplayName , CancellationToken cancellationToken )
837+ => ReportSolutionFilesAsync ( newSolution , Interlocked . Increment ( ref _solutionUpdateId ) , operationDisplayName , cancellationToken ) ;
838+
839+ private async Task ReportSolutionFilesAsync ( Solution solution , int updateId , string operationDisplayName , CancellationToken cancellationToken )
840+ {
841+ _logger . LogDebug ( "Solution after {Operation}: v{Version}" , operationDisplayName , updateId ) ;
842+
843+ if ( ! _logger . IsEnabled ( LogLevel . Trace ) )
844+ {
845+ return ;
846+ }
847+
848+ foreach ( var project in solution . Projects )
849+ {
850+ _logger . LogDebug ( " Project: {Path}" , project . FilePath ) ;
851+
852+ foreach ( var document in project . Documents )
853+ {
854+ await InspectDocumentAsync ( document , "Document" ) . ConfigureAwait ( false ) ;
855+ }
856+
857+ foreach ( var document in project . AdditionalDocuments )
858+ {
859+ await InspectDocumentAsync ( document , "Additional" ) . ConfigureAwait ( false ) ;
860+ }
861+
862+ foreach ( var document in project . AnalyzerConfigDocuments )
863+ {
864+ await InspectDocumentAsync ( document , "Config" ) . ConfigureAwait ( false ) ;
865+ }
866+ }
867+
868+ async ValueTask InspectDocumentAsync ( TextDocument document , string kind )
869+ {
870+ var text = await document . GetTextAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
871+ _logger . LogDebug ( " {Kind}: {FilePath} [{Checksum}]" , kind , document . FilePath , Convert . ToBase64String ( text . GetChecksum ( ) . ToArray ( ) ) ) ;
872+ }
873+ }
801874 }
802875}
0 commit comments