@@ -27,7 +27,6 @@ partial class ServerHotReloadProcessor : IServerProcessor, IDisposable
2727 private static readonly StringComparer _pathsComparer = OperatingSystem . IsWindows ( ) ? StringComparer . OrdinalIgnoreCase : StringComparer . Ordinal ;
2828 private static readonly StringComparison _pathsComparison = OperatingSystem . IsWindows ( ) ? StringComparison . OrdinalIgnoreCase : StringComparison . Ordinal ;
2929
30- private bool _sendUpdatesThruDebugger ;
3130 private FileSystemWatcher [ ] ? _solutionWatchers ;
3231 private IDisposable ? _solutionWatcherEventsDisposable ;
3332
@@ -36,12 +35,24 @@ partial class ServerHotReloadProcessor : IServerProcessor, IDisposable
3635 private WatchHotReloadService ? _hotReloadService ;
3736 private IReporter _reporter = new Reporter ( ) ;
3837
39- private bool InitializeMetadataUpdater ( ConfigureServer configureServer )
38+ private bool _useRoslynHotReload ;
39+ private bool _useHotReloadThruDebugger ;
40+
41+ private bool InitializeMetadataUpdater ( ConfigureServer configureServer , Dictionary < string , string > properties )
4042 {
41- if ( configureServer . EnableMetadataUpdates )
43+ _ = bool . TryParse ( _remoteControlServer . GetServerConfiguration ( "metadata-updates" ) , out _useRoslynHotReload ) ;
44+
45+ _useRoslynHotReload = _useRoslynHotReload || configureServer . EnableMetadataUpdates ;
46+ _useHotReloadThruDebugger = configureServer . EnableHotReloadThruDebugger ;
47+
48+ if ( _useRoslynHotReload )
4249 {
43- _sendUpdatesThruDebugger = configureServer . EnableHotReloadThruDebugger ;
44- InitializeInner ( configureServer ) ;
50+ // Assembly registrations must be done before the workspace is initialized
51+ // Not doing so will cause the roslyn msbuild workspace to fail to load because
52+ // of a missing path on assemblies loaded from a memory stream.
53+ CompilationWorkspaceProvider . RegisterAssemblyLoader ( ) ;
54+
55+ InitializeInner ( configureServer , properties ) ;
4556
4657 return true ;
4758 }
@@ -51,47 +62,28 @@ private bool InitializeMetadataUpdater(ConfigureServer configureServer)
5162 }
5263 }
5364
54- private void InitializeInner ( ConfigureServer configureServer )
65+ private void InitializeInner ( ConfigureServer configureServer , Dictionary < string , string > properties )
5566 {
56- try
67+ if ( Assembly . Load ( "Microsoft.CodeAnalysis.Workspaces" ) is { } wsAsm )
5768 {
58- // Assembly registrations must be done before the workspace is initialized
59- // Not doing so will cause the roslyn msbuild workspace to fail to load because
60- // of a missing path on assemblies loaded from a memory stream.
61- CompilationWorkspaceProvider . RegisterAssemblyLoader ( ) ;
62-
63- if ( Assembly . Load ( "Microsoft.CodeAnalysis.Workspaces" ) is { } wsAsm )
69+ // If this assembly was loaded from a stream, it will not have a location.
70+ // This will indicate that the assembly loader from CompilationWorkspaceProvider
71+ // has been registered too late.
72+ if ( string . IsNullOrEmpty ( wsAsm . Location ) )
6473 {
65- // If this assembly was loaded from a stream, it will not have a location.
66- // This will indicate that the assembly loader from CompilationWorkspaceProvider
67- // has been registered too late.
68- if ( string . IsNullOrEmpty ( wsAsm . Location ) )
69- {
70- throw new InvalidOperationException ( "Microsoft.CodeAnalysis.Workspaces was loaded from a stream and must loaded from a known path" ) ;
71- }
74+ throw new InvalidOperationException ( "Microsoft.CodeAnalysis.Workspaces was loaded from a stream and must loaded from a known path" ) ;
7275 }
73-
74- CompilationWorkspaceProvider . InitializeRoslyn ( Path . GetDirectoryName ( configureServer . ProjectPath ) ) ;
75-
76- _initializeTask = Task . Run ( InitializeAsync , CancellationToken . None ) ;
7776 }
78- catch
79- {
80- _ = _remoteControlServer . SendFrame ( new HotReloadWorkspaceLoadResult { WorkspaceInitialized = false } ) ;
81- _ = Notify ( HotReloadEvent . Disabled ) ;
8277
83- throw ;
84- }
78+ CompilationWorkspaceProvider . InitializeRoslyn ( Path . GetDirectoryName ( configureServer . ProjectPath ) ) ;
8579
86- async Task < ( Solution , WatchHotReloadService ) > InitializeAsync ( )
80+ _initializeTask = Task . Run (
81+ async ( ) =>
8782 {
8883 try
8984 {
9085 await Notify ( HotReloadEvent . Initializing ) ;
9186
92- // Clone the properties from the ConfigureServer
93- var properties = configureServer . MSBuildProperties . ToDictionary ( ) ;
94-
9587 // Flag the current build as created for hot reload, which allows for running targets or settings
9688 // props/items in the context of the hot reload workspace.
9789 properties [ "UnoIsHotReloadHost" ] = "True" ;
@@ -141,31 +133,32 @@ private void InitializeInner(ConfigureServer configureServer)
141133
142134 throw ;
143135 }
144- }
136+ } ,
137+ CancellationToken . None ) ;
145138 }
146139
147140 private void ObserveSolutionPaths ( Solution solution , params string ? [ ] excludedDir )
148141 {
149- var observedPaths = solution
150- . Projects
151- . SelectMany ( project =>
152- {
153- var projectDir = Path . GetDirectoryName ( project . FilePath ) ;
154- ImmutableArray < string > excludedProjectDir = [ .. from dir in excludedDir where dir is not null select Path . Combine ( projectDir ! , dir ) . TrimEnd ( Path . DirectorySeparatorChar , Path . AltDirectorySeparatorChar ) ] ;
155-
156- var paths = project
157- . Documents
158- . Select ( d => d . FilePath )
159- . Concat ( project . AdditionalDocuments . Select ( d => d . FilePath ) )
160- . Select ( Path . GetDirectoryName )
161- . Where ( path => path is not null && ! excludedProjectDir . Any ( dir => path . StartsWith ( dir , _pathsComparison ) ) )
162- . Distinct ( )
163- . ToArray ( ) ;
164-
165- return paths ;
166- } )
167- . Distinct ( )
168- . ToArray ( ) ;
142+ var observedPaths =
143+ solution . Projects
144+ . SelectMany ( project =>
145+ {
146+ var projectDir = Path . GetDirectoryName ( project . FilePath ) ;
147+ ImmutableArray < string > excludedProjectDir = [ .. from dir in excludedDir where dir is not null select Path . Combine ( projectDir ! , dir ) . TrimEnd ( Path . DirectorySeparatorChar , Path . AltDirectorySeparatorChar ) ] ;
148+
149+ var paths = project
150+ . Documents
151+ . Select ( d => d . FilePath )
152+ . Concat ( project . AdditionalDocuments . Select ( d => d . FilePath ) )
153+ . Select ( Path . GetDirectoryName )
154+ . Where ( path => path is not null && ! excludedProjectDir . Any ( dir => path . StartsWith ( dir , _pathsComparison ) ) )
155+ . Distinct ( )
156+ . ToArray ( ) ;
157+
158+ return paths ;
159+ } )
160+ . Distinct ( )
161+ . ToArray ( ) ;
169162
170163#if DEBUG
171164 Console . WriteLine ( $ "Observing paths { string . Join ( ", " , observedPaths ) } ") ;
@@ -277,46 +270,40 @@ private async ValueTask ProcessSolutionChanged(HotReloadServerOperation hotReloa
277270 _reporter . Output ( $ "Found { updates . Length } metadata updates after { sw . Elapsed } ") ;
278271 sw . Stop ( ) ;
279272
280- switch ( rudeEdits . IsEmpty , updates . IsEmpty )
281- {
282- case ( true , true ) :
283- {
284- var compilationErrors = GetCompilationErrors ( solution , ct ) ;
285- if ( compilationErrors . IsEmpty )
286- {
287- _reporter . Output ( "No hot reload changes to apply." ) ;
288- await hotReload . Complete ( HotReloadServerResult . NoChanges ) ;
289- }
290- else
291- {
292- await hotReload . Complete ( HotReloadServerResult . Failed ) ;
293- }
294- }
295- break ;
296273
297- case ( false , _ ) :
298- {
299- // Rude edit.
300- _reporter . Output ( "Unable to apply hot reload because of a rude edit." ) ;
301- foreach ( var diagnostic in hotReloadDiagnostics )
302- {
303- _reporter . Verbose ( CSharpDiagnosticFormatter . Instance . Format ( diagnostic , CultureInfo . InvariantCulture ) ) ;
304- }
274+ if ( rudeEdits . IsEmpty && updates . IsEmpty )
275+ {
276+ var compilationErrors = GetCompilationErrors ( solution , ct ) ;
277+ if ( compilationErrors . IsEmpty )
278+ {
279+ _reporter . Output ( "No hot reload changes to apply." ) ;
280+ await hotReload . Complete ( HotReloadServerResult . NoChanges ) ;
281+ }
282+ else
283+ {
284+ await hotReload . Complete ( HotReloadServerResult . Failed ) ;
285+ }
305286
306- await hotReload . Complete ( HotReloadServerResult . RudeEdit ) ;
307- }
308- break ;
287+ return ;
288+ }
309289
310- //case (true, false):
311- default :
312- {
313- await SendUpdates ( updates ) ;
290+ if ( ! rudeEdits . IsEmpty )
291+ {
292+ // Rude edit.
293+ _reporter . Output ( "Unable to apply hot reload because of a rude edit." ) ;
294+ foreach ( var diagnostic in hotReloadDiagnostics )
295+ {
296+ _reporter . Verbose ( CSharpDiagnosticFormatter . Instance . Format ( diagnostic , CultureInfo . InvariantCulture ) ) ;
297+ }
314298
315- await hotReload . Complete ( HotReloadServerResult . Success ) ;
316- }
317- break ;
299+ await hotReload . Complete ( HotReloadServerResult . RudeEdit ) ;
300+ return ;
318301 }
319302
303+ await SendUpdates ( updates ) ;
304+
305+ await hotReload . Complete ( HotReloadServerResult . Success ) ;
306+
320307 async Task SendUpdates ( ImmutableArray < WatchHotReloadService . Update > updates )
321308 {
322309#if DEBUG
@@ -325,7 +312,7 @@ async Task SendUpdates(ImmutableArray<WatchHotReloadService.Update> updates)
325312
326313 for ( var i = 0 ; i < updates . Length ; i ++ )
327314 {
328- if ( _sendUpdatesThruDebugger )
315+ if ( _useHotReloadThruDebugger )
329316 {
330317 await _remoteControlServer . SendMessageToIDEAsync (
331318 new Uno . UI . RemoteControl . Messaging . IdeChannel . HotReloadThruDebuggerIdeMessage (
0 commit comments