diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueLanguageService.cs index c812ac036bacc..6e2686e2572c3 100644 --- a/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueLanguageService.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; +using Microsoft.VisualStudio.Debugger.Contracts.HotReload; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue @@ -22,6 +23,7 @@ internal sealed class EditAndContinueLanguageService : IEditAndContinueSolutionP private static readonly ActiveStatementSpanProvider s_noActiveStatementSpanProvider = (_, _, _) => ValueTaskFactory.FromResult(ImmutableArray.Empty); + private readonly Lazy _debuggerService; private readonly IDiagnosticAnalyzerService _diagnosticService; private readonly EditAndContinueDiagnosticUpdateSource _diagnosticUpdateSource; @@ -41,10 +43,12 @@ internal sealed class EditAndContinueLanguageService : IEditAndContinueSolutionP /// public EditAndContinueLanguageService( Lazy workspaceProvider, + Lazy debuggerService, IDiagnosticAnalyzerService diagnosticService, EditAndContinueDiagnosticUpdateSource diagnosticUpdateSource) { WorkspaceProvider = workspaceProvider; + _debuggerService = debuggerService; _diagnosticService = diagnosticService; _diagnosticUpdateSource = diagnosticUpdateSource; } @@ -71,7 +75,7 @@ internal void Disable() /// /// Called by the debugger when a debugging session starts and managed debugging is being used. /// - public async ValueTask StartSessionAsync(IManagedEditAndContinueDebuggerService debugger, CancellationToken cancellationToken) + public async ValueTask StartSessionAsync(CancellationToken cancellationToken) { IsSessionActive = true; @@ -89,7 +93,7 @@ public async ValueTask StartSessionAsync(IManagedEditAndContinueDebuggerService _debuggingSession = await proxy.StartDebuggingSessionAsync( solution, - debugger, + _debuggerService.Value, captureMatchingDocuments: openedDocumentIds, captureAllMatchingDocuments: false, reportDiagnostics: true, @@ -115,7 +119,7 @@ public async ValueTask EnterBreakStateAsync(CancellationToken cancellationToken) try { - await session.BreakStateChangedAsync(_diagnosticService, inBreakState: true, cancellationToken).ConfigureAwait(false); + await session.BreakStateOrCapabilitiesChangedAsync(_diagnosticService, inBreakState: true, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { @@ -139,7 +143,7 @@ public async ValueTask ExitBreakStateAsync(CancellationToken cancellationToken) try { - await session.BreakStateChangedAsync(_diagnosticService, inBreakState: false, cancellationToken).ConfigureAwait(false); + await session.BreakStateOrCapabilitiesChangedAsync(_diagnosticService, inBreakState: false, cancellationToken).ConfigureAwait(false); GetActiveStatementTrackingService().EndTracking(); } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) @@ -149,6 +153,23 @@ public async ValueTask ExitBreakStateAsync(CancellationToken cancellationToken) } } + public async ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken) + { + if (_disabled) + { + return; + } + + try + { + await GetDebuggingSession().BreakStateOrCapabilitiesChangedAsync(_diagnosticService, inBreakState: null, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + _disabled = true; + } + } + public async ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) { try @@ -239,26 +260,40 @@ public async ValueTask HasChangesAsync(string? sourceFilePath, Cancellatio } } - public async ValueTask<(ManagedModuleUpdates updates, ImmutableArray diagnostics, ImmutableArray<(DocumentId DocumentId, ImmutableArray Diagnostics)> rudeEdits, DiagnosticData? syntaxError, Solution? solution)> - GetUpdatesAsync(bool trackActiveStatements, CancellationToken cancellationToken) + public async ValueTask GetEditAndContinueUpdatesAsync(CancellationToken cancellationToken) { if (_disabled) { - return (new ManagedModuleUpdates(ManagedModuleUpdateStatus.None, ImmutableArray.Empty), - ImmutableArray.Empty, - ImmutableArray<(DocumentId DocumentId, ImmutableArray Diagnostics)>.Empty, - syntaxError: null, - solution: null); + return new ManagedModuleUpdates(ManagedModuleUpdateStatus.None, ImmutableArray.Empty); } var solution = GetCurrentCompileTimeSolution(); - var activeStatementSpanProvider = trackActiveStatements ? GetActiveStatementSpanProvider(solution) : s_noActiveStatementSpanProvider; - var (updates, diagnostics, rudeEdits, syntaxError) = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, _diagnosticService, _diagnosticUpdateSource, cancellationToken).ConfigureAwait(false); + var activeStatementSpanProvider = GetActiveStatementSpanProvider(solution); + var (updates, _, _, _) = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, activeStatementSpanProvider, _diagnosticService, _diagnosticUpdateSource, cancellationToken).ConfigureAwait(false); _pendingUpdatedSolution = solution; - return (updates, diagnostics, rudeEdits, syntaxError, solution); + return updates; + } + + public async ValueTask GetHotReloadUpdatesAsync(CancellationToken cancellationToken) + { + if (_disabled) + { + return new ManagedHotReloadUpdates(ImmutableArray.Empty, ImmutableArray.Empty); + } + + var solution = GetCurrentCompileTimeSolution(); + var (moduleUpdates, diagnosticData, rudeEdits, syntaxError) = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, s_noActiveStatementSpanProvider, _diagnosticService, _diagnosticUpdateSource, cancellationToken).ConfigureAwait(false); + _pendingUpdatedSolution = solution; + + var updates = moduleUpdates.Updates.SelectAsArray( + update => new ManagedHotReloadUpdate(update.Module, update.ILDelta, update.MetadataDelta)); + + var diagnostics = await EmitSolutionUpdateResults.GetHotReloadDiagnosticsAsync(solution, diagnosticData, rudeEdits, syntaxError, cancellationToken).ConfigureAwait(false); + + return new ManagedHotReloadUpdates(updates, diagnostics); } - public async Task GetCurrentActiveStatementPositionAsync(ManagedInstructionId instruction, CancellationToken cancellationToken) + public async ValueTask GetCurrentActiveStatementPositionAsync(ManagedInstructionId instruction, CancellationToken cancellationToken) { try { @@ -277,7 +312,7 @@ public async ValueTask HasChangesAsync(string? sourceFilePath, Cancellatio } } - public async Task IsActiveStatementInExceptionRegionAsync(ManagedInstructionId instruction, CancellationToken cancellationToken) + public async ValueTask IsActiveStatementInExceptionRegionAsync(ManagedInstructionId instruction, CancellationToken cancellationToken) { try { diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs index fdfac18394a87..c2344e52eae49 100644 --- a/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedEditAndContinueLanguageService.cs @@ -22,7 +22,6 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue [ExportMetadata("UIContext", EditAndContinueUIContext.EncCapableProjectExistsInWorkspaceUIContextString)] internal sealed class ManagedEditAndContinueLanguageService : IManagedEditAndContinueLanguageService, IEditAndContinueSolutionProvider { - private readonly Lazy _debuggerService; private readonly EditAndContinueLanguageService _encService; /// @@ -37,8 +36,7 @@ public ManagedEditAndContinueLanguageService( EditAndContinueDiagnosticUpdateSource diagnosticUpdateSource, Lazy debuggerService) { - _encService = new EditAndContinueLanguageService(workspaceProvider, diagnosticService, diagnosticUpdateSource); - _debuggerService = debuggerService; + _encService = new EditAndContinueLanguageService(workspaceProvider, debuggerService, diagnosticService, diagnosticUpdateSource); } public EditAndContinueLanguageService Service => _encService; @@ -54,7 +52,7 @@ public Task StartDebuggingAsync(DebugSessionFlags flags, CancellationToken cance return Task.CompletedTask; } - return _encService.StartSessionAsync(_debuggerService.Value, cancellationToken).AsTask(); + return _encService.StartSessionAsync(cancellationToken).AsTask(); } public Task EnterBreakStateAsync(CancellationToken cancellationToken) @@ -75,14 +73,14 @@ public Task StopDebuggingAsync(CancellationToken cancellationToken) public Task HasChangesAsync(string? sourceFilePath, CancellationToken cancellationToken) => _encService.HasChangesAsync(sourceFilePath, cancellationToken).AsTask(); - public async Task GetManagedModuleUpdatesAsync(CancellationToken cancellationToken) - => (await _encService.GetUpdatesAsync(trackActiveStatements: true, cancellationToken).ConfigureAwait(false)).updates; + public Task GetManagedModuleUpdatesAsync(CancellationToken cancellationToken) + => _encService.GetEditAndContinueUpdatesAsync(cancellationToken).AsTask(); public Task GetCurrentActiveStatementPositionAsync(ManagedInstructionId instruction, CancellationToken cancellationToken) - => _encService.GetCurrentActiveStatementPositionAsync(instruction, cancellationToken); + => _encService.GetCurrentActiveStatementPositionAsync(instruction, cancellationToken).AsTask(); public Task IsActiveStatementInExceptionRegionAsync(ManagedInstructionId instruction, CancellationToken cancellationToken) - => _encService.IsActiveStatementInExceptionRegionAsync(instruction, cancellationToken); + => _encService.IsActiveStatementInExceptionRegionAsync(instruction, cancellationToken).AsTask(); public event Action SolutionCommitted { diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedHotReloadLanguageService.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedHotReloadLanguageService.cs index 3f21bce2926d6..84ebecb0811bb 100644 --- a/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedHotReloadLanguageService.cs +++ b/src/EditorFeatures/Core/Implementation/EditAndContinue/ManagedHotReloadLanguageService.cs @@ -47,7 +47,6 @@ public Task PrepareModuleForUpdateAsync(Guid module, CancellationToken cancellat } private readonly EditAndContinueLanguageService _encService; - private readonly DebuggerService _debuggerService; /// /// Import and lazily so that the host does not need to implement them @@ -61,29 +60,16 @@ public ManagedHotReloadLanguageService( EditAndContinueDiagnosticUpdateSource diagnosticUpdateSource, Lazy hotReloadService) { - _encService = new EditAndContinueLanguageService(workspaceProvider, diagnosticService, diagnosticUpdateSource); - _debuggerService = new DebuggerService(hotReloadService); + _encService = new EditAndContinueLanguageService(workspaceProvider, new Lazy(() => new DebuggerService(hotReloadService)), diagnosticService, diagnosticUpdateSource); } public EditAndContinueLanguageService Service => _encService; public ValueTask StartSessionAsync(CancellationToken cancellationToken) - => _encService.StartSessionAsync(_debuggerService, cancellationToken); + => _encService.StartSessionAsync(cancellationToken); - public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) - { - var (moduleUpdates, diagnosticData, rudeEdits, syntaxError, solution) = await _encService.GetUpdatesAsync(trackActiveStatements: false, cancellationToken).ConfigureAwait(false); - if (solution == null) - { - return new ManagedHotReloadUpdates(ImmutableArray.Empty, ImmutableArray.Empty); - } - - var updates = moduleUpdates.Updates.SelectAsArray( - update => new ManagedHotReloadUpdate(update.Module, update.ILDelta, update.MetadataDelta)); - var diagnostics = await EmitSolutionUpdateResults.GetHotReloadDiagnosticsAsync(solution, diagnosticData, rudeEdits, syntaxError, cancellationToken).ConfigureAwait(false); - - return new ManagedHotReloadUpdates(updates, diagnostics); - } + public ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => _encService.GetHotReloadUpdatesAsync(cancellationToken); public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) => _encService.CommitUpdatesAsync(cancellationToken); diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 536e0c2c0b522..8a11c6a78ca24 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -171,7 +171,7 @@ private void EnterBreakState( ImmutableArray documentsWithRudeEdits = default) { _debuggerService.GetActiveStatementsImpl = () => activeStatements.NullToEmpty(); - session.BreakStateChanged(inBreakState: true, out var documentsToReanalyze); + session.BreakStateOrCapabilitiesChanged(inBreakState: true, out var documentsToReanalyze); AssertEx.Equal(documentsWithRudeEdits.NullToEmpty(), documentsToReanalyze); } @@ -180,7 +180,15 @@ private void ExitBreakState( ImmutableArray documentsWithRudeEdits = default) { _debuggerService.GetActiveStatementsImpl = () => ImmutableArray.Empty; - session.BreakStateChanged(inBreakState: false, out var documentsToReanalyze); + session.BreakStateOrCapabilitiesChanged(inBreakState: false, out var documentsToReanalyze); + AssertEx.Equal(documentsWithRudeEdits.NullToEmpty(), documentsToReanalyze); + } + + private static void CapabilitiesChanged( + DebuggingSession session, + ImmutableArray documentsWithRudeEdits = default) + { + session.BreakStateOrCapabilitiesChanged(inBreakState: null, out var documentsToReanalyze); AssertEx.Equal(documentsWithRudeEdits.NullToEmpty(), documentsToReanalyze); } @@ -1696,8 +1704,9 @@ public async Task Project_Add() EndDebuggingSession(debuggingSession); } - [Fact] - public async Task Capabilities() + [Theory] + [CombinatorialData] + public async Task Capabilities(bool breakState) { var source1 = "class C { void M() { } }"; var source2 = "[System.Obsolete]class C { void M() { } }"; @@ -1720,21 +1729,39 @@ public async Task Capabilities() var diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetDocument(documentId), s_noActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics); - EnterBreakState(debuggingSession); + if (breakState) + { + EnterBreakState(debuggingSession); + } diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetDocument(documentId), s_noActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics); // attach to additional processes - at least one process that does not allow updating custom attributes: - ExitBreakState(debuggingSession); + if (breakState) + { + ExitBreakState(debuggingSession); + } + _debuggerService.GetCapabilitiesImpl = () => ImmutableArray.Create("Baseline"); - EnterBreakState(debuggingSession); + + if (breakState) + { + EnterBreakState(debuggingSession); + } + else + { + CapabilitiesChanged(debuggingSession); + } diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetDocument(documentId), s_noActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC0101: " + string.Format(FeaturesResources.Updating_the_attributes_of_0_requires_restarting_the_application_because_it_is_not_supported_by_the_runtime, FeaturesResources.class_) }, diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}")); - ExitBreakState(debuggingSession, documentsWithRudeEdits: ImmutableArray.Create(documentId)); + if (breakState) + { + ExitBreakState(debuggingSession, documentsWithRudeEdits: ImmutableArray.Create(documentId)); + } diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetDocument(documentId), s_noActiveSpans, CancellationToken.None); AssertEx.Equal(new[] { "ENC0101: " + string.Format(FeaturesResources.Updating_the_attributes_of_0_requires_restarting_the_application_because_it_is_not_supported_by_the_runtime, FeaturesResources.class_) }, @@ -1743,12 +1770,23 @@ public async Task Capabilities() // detach from processes that do not allow updating custom attributes: _debuggerService.GetCapabilitiesImpl = () => ImmutableArray.Create("Baseline", "ChangeCustomAttributes"); - EnterBreakState(debuggingSession, documentsWithRudeEdits: ImmutableArray.Create(documentId)); + if (breakState) + { + EnterBreakState(debuggingSession, documentsWithRudeEdits: ImmutableArray.Create(documentId)); + } + else + { + CapabilitiesChanged(debuggingSession, documentsWithRudeEdits: ImmutableArray.Create(documentId)); + } diagnostics = await service.GetDocumentDiagnosticsAsync(solution.GetDocument(documentId), s_noActiveSpans, CancellationToken.None); AssertEx.Empty(diagnostics); - ExitBreakState(debuggingSession); + if (breakState) + { + ExitBreakState(debuggingSession); + } + EndDebuggingSession(debuggingSession); } @@ -3922,7 +3960,7 @@ public async Task Disposal() await Assert.ThrowsAsync(async () => await debuggingSession.EmitSolutionUpdateAsync(solution, s_noActiveSpans, CancellationToken.None)); await Assert.ThrowsAsync(async () => await debuggingSession.GetCurrentActiveStatementPositionAsync(solution, s_noActiveSpans, instructionId: default, CancellationToken.None)); await Assert.ThrowsAsync(async () => await debuggingSession.IsActiveStatementInExceptionRegionAsync(solution, instructionId: default, CancellationToken.None)); - Assert.Throws(() => debuggingSession.BreakStateChanged(inBreakState: true, out _)); + Assert.Throws(() => debuggingSession.BreakStateOrCapabilitiesChanged(inBreakState: true, out _)); Assert.Throws(() => debuggingSession.DiscardSolutionUpdate()); Assert.Throws(() => debuggingSession.CommitSolutionUpdate(out _)); Assert.Throws(() => debuggingSession.EndSession(out _, out _)); diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 09ad90ce903c5..7cad2a83b113f 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -157,13 +157,13 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) // BreakStateChanged - mockEncService.BreakStateChangesImpl = (bool inBreakState, out ImmutableArray documentsToReanalyze) => + mockEncService.BreakStateOrCapabilitiesChangedImpl = (bool? inBreakState, out ImmutableArray documentsToReanalyze) => { Assert.True(inBreakState); documentsToReanalyze = ImmutableArray.Create(document.Id); }; - await sessionProxy.BreakStateChangedAsync(mockDiagnosticService, inBreakState: true, CancellationToken.None).ConfigureAwait(false); + await sessionProxy.BreakStateOrCapabilitiesChangedAsync(mockDiagnosticService, inBreakState: true, CancellationToken.None).ConfigureAwait(false); VerifyReanalyzeInvocation(ImmutableArray.Create(document.Id)); var activeStatement = (await remoteDebuggeeModuleMetadataProvider!.GetActiveStatementsAsync(CancellationToken.None).ConfigureAwait(false)).Single(); diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs index e45051d1daf98..c9680f115ef02 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/MockEditAndContinueWorkspaceService.cs @@ -31,7 +31,7 @@ internal class MockEditAndContinueWorkspaceService : IEditAndContinueWorkspaceSe public Func? IsActiveStatementInExceptionRegionImpl; public Action? OnSourceFileUpdatedImpl; public ActionOut>? CommitSolutionUpdateImpl; - public ActionOut>? BreakStateChangesImpl; + public ActionOut>? BreakStateOrCapabilitiesChangedImpl; public Action? DiscardSolutionUpdateImpl; public Func>? GetDocumentDiagnosticsImpl; @@ -41,10 +41,10 @@ public MockEditAndContinueWorkspaceService() { } - public void BreakStateChanged(DebuggingSessionId sessionId, bool inBreakState, out ImmutableArray documentsToReanalyze) + public void BreakStateOrCapabilitiesChanged(DebuggingSessionId sessionId, bool? inBreakState, out ImmutableArray documentsToReanalyze) { documentsToReanalyze = ImmutableArray.Empty; - BreakStateChangesImpl?.Invoke(inBreakState, out documentsToReanalyze); + BreakStateOrCapabilitiesChangedImpl?.Invoke(inBreakState, out documentsToReanalyze); } public void CommitSolutionUpdate(DebuggingSessionId sessionId, out ImmutableArray documentsToReanalyze) diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index ae27381802f1d..76094449f218b 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -109,7 +109,14 @@ internal DebuggingSession( Id = id; DebuggerService = debuggerService; LastCommittedSolution = new CommittedSolution(this, solution, initialDocumentStates); - EditSession = new EditSession(this, nonRemappableRegions: ImmutableDictionary>.Empty, _editSessionTelemetry, inBreakState: false); + + EditSession = new EditSession( + this, + nonRemappableRegions: ImmutableDictionary>.Empty, + _editSessionTelemetry, + lazyActiveStatementMap: null, + inBreakState: false); + ReportDiagnostics = reportDiagnostics; } @@ -192,15 +199,21 @@ public void EndSession(out ImmutableArray documentsToReanalyze, out Dispose(); } - public void BreakStateChanged(bool inBreakState, out ImmutableArray documentsToReanalyze) + public void BreakStateOrCapabilitiesChanged(bool? inBreakState, out ImmutableArray documentsToReanalyze) => RestartEditSession(nonRemappableRegions: null, inBreakState, out documentsToReanalyze); - internal void RestartEditSession(ImmutableDictionary>? nonRemappableRegions, bool inBreakState, out ImmutableArray documentsToReanalyze) + internal void RestartEditSession(ImmutableDictionary>? nonRemappableRegions, bool? inBreakState, out ImmutableArray documentsToReanalyze) { ThrowIfDisposed(); EndEditSession(out documentsToReanalyze); - EditSession = new EditSession(this, nonRemappableRegions ?? EditSession.NonRemappableRegions, EditSession.Telemetry, inBreakState); + + EditSession = new EditSession( + this, + nonRemappableRegions ?? EditSession.NonRemappableRegions, + EditSession.Telemetry, + (inBreakState == null) ? EditSession.BaseActiveStatements : null, + inBreakState ?? EditSession.InBreakState); } private ImmutableArray GetBaselineModuleReaders() diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index 33e87e7ff3f50..6f63b0aa7ef59 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -139,11 +139,11 @@ public void EndDebuggingSession(DebuggingSessionId sessionId, out ImmutableArray debuggingSession.EndSession(out documentsToReanalyze, out var telemetryData); } - public void BreakStateChanged(DebuggingSessionId sessionId, bool inBreakState, out ImmutableArray documentsToReanalyze) + public void BreakStateOrCapabilitiesChanged(DebuggingSessionId sessionId, bool? inBreakState, out ImmutableArray documentsToReanalyze) { var debuggingSession = TryGetDebuggingSession(sessionId); Contract.ThrowIfNull(debuggingSession); - debuggingSession.BreakStateChanged(inBreakState, out documentsToReanalyze); + debuggingSession.BreakStateOrCapabilitiesChanged(inBreakState, out documentsToReanalyze); } public ValueTask> GetDocumentDiagnosticsAsync(Document document, ActiveStatementSpanProvider activeStatementSpanProvider, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index e7e1815f15b3d..0f653a854b953 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -98,6 +98,7 @@ internal EditSession( DebuggingSession debuggingSession, ImmutableDictionary> nonRemappableRegions, EditSessionTelemetry telemetry, + AsyncLazy? lazyActiveStatementMap, bool inBreakState) { DebuggingSession = debuggingSession; @@ -105,9 +106,9 @@ internal EditSession( Telemetry = telemetry; InBreakState = inBreakState; - BaseActiveStatements = inBreakState ? + BaseActiveStatements = lazyActiveStatementMap ?? (inBreakState ? new AsyncLazy(GetBaseActiveStatementsAsync, cacheResult: true) : - new AsyncLazy(ActiveStatementsMap.Empty); + new AsyncLazy(ActiveStatementsMap.Empty)); Capabilities = new AsyncLazy(GetCapabilitiesAsync, cacheResult: true); Analyses = new EditAndContinueDocumentAnalysesCache(BaseActiveStatements, Capabilities); diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs index 6c7491fbfd0e6..c5c837c80f100 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueWorkspaceService.cs @@ -23,7 +23,7 @@ internal interface IEditAndContinueWorkspaceService : IWorkspaceService void OnSourceFileUpdated(Document document); ValueTask StartDebuggingSessionAsync(Solution solution, IManagedEditAndContinueDebuggerService debuggerService, ImmutableArray captureMatchingDocuments, bool captureAllMatchingDocuments, bool reportDiagnostics, CancellationToken cancellationToken); - void BreakStateChanged(DebuggingSessionId sessionId, bool inBreakState, out ImmutableArray documentsToReanalyze); + void BreakStateOrCapabilitiesChanged(DebuggingSessionId sessionId, bool? inBreakState, out ImmutableArray documentsToReanalyze); void EndDebuggingSession(DebuggingSessionId sessionId, out ImmutableArray documentsToReanalyze); ValueTask IsActiveStatementInExceptionRegionAsync(DebuggingSessionId sessionId, Solution solution, ManagedInstructionId instructionId, CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs index 10b13294f58ec..a8a822b1b84b8 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/IRemoteEditAndContinueService.cs @@ -40,7 +40,7 @@ internal interface ICallback /// /// Returns ids of documents for which diagnostics need to be refreshed in-proc. /// - ValueTask> BreakStateChangedAsync(DebuggingSessionId sessionId, bool isBreakState, CancellationToken cancellationToken); + ValueTask> BreakStateOrCapabilitiesChangedAsync(DebuggingSessionId sessionId, bool? isBreakState, CancellationToken cancellationToken); /// /// Returns ids of documents for which diagnostics need to be refreshed in-proc. diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs index bcc49b31ecada..684876e16014f 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs @@ -41,19 +41,19 @@ public void Dispose() private IEditAndContinueWorkspaceService GetLocalService() => _workspace.Services.GetRequiredService(); - public async ValueTask BreakStateChangedAsync(IDiagnosticAnalyzerService diagnosticService, bool inBreakState, CancellationToken cancellationToken) + public async ValueTask BreakStateOrCapabilitiesChangedAsync(IDiagnosticAnalyzerService diagnosticService, bool? inBreakState, CancellationToken cancellationToken) { ImmutableArray documentsToReanalyze; var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) { - GetLocalService().BreakStateChanged(_sessionId, inBreakState, out documentsToReanalyze); + GetLocalService().BreakStateOrCapabilitiesChanged(_sessionId, inBreakState, out documentsToReanalyze); } else { var documentsToReanalyzeOpt = await client.TryInvokeAsync>( - (service, cancallationToken) => service.BreakStateChangedAsync(_sessionId, inBreakState, cancellationToken), + (service, cancallationToken) => service.BreakStateOrCapabilitiesChangedAsync(_sessionId, inBreakState, cancellationToken), cancellationToken).ConfigureAwait(false); documentsToReanalyze = documentsToReanalyzeOpt.HasValue ? documentsToReanalyzeOpt.Value : ImmutableArray.Empty; diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs index 59119be940b13..47941c1013ad5 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/RemoteEditAndContinueService.cs @@ -80,11 +80,11 @@ public ValueTask StartDebuggingSessionAsync(PinnedSolutionIn /// /// Remote API. /// - public ValueTask> BreakStateChangedAsync(DebuggingSessionId sessionId, bool inBreakState, CancellationToken cancellationToken) + public ValueTask> BreakStateOrCapabilitiesChangedAsync(DebuggingSessionId sessionId, bool? inBreakState, CancellationToken cancellationToken) { return RunServiceAsync(cancellationToken => { - GetService().BreakStateChanged(sessionId, inBreakState, out var documentsToReanalyze); + GetService().BreakStateOrCapabilitiesChanged(sessionId, inBreakState, out var documentsToReanalyze); return new ValueTask>(documentsToReanalyze); }, cancellationToken); }