From 420c78c91b22c588f854d9bf076e70ddc58a1b4e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Dec 2022 14:49:22 -0800 Subject: [PATCH 001/151] Remove --- .../TaskList/TaskListIncrementalAnalyzer.cs | 97 ----------- .../TaskListIncrementalAnalyzerProvider.cs | 29 ---- .../Features/TaskList/TaskListListener.cs | 163 ------------------ 3 files changed, 289 deletions(-) delete mode 100644 src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs delete mode 100644 src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzerProvider.cs delete mode 100644 src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs diff --git a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs deleted file mode 100644 index 9dcebad0ce45f..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzer.cs +++ /dev/null @@ -1,97 +0,0 @@ -#if false - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; - -namespace Microsoft.CodeAnalysis.TaskList -{ - internal sealed class TaskListIncrementalAnalyzer : IncrementalAnalyzerBase - { - private readonly object _gate = new(); - private ImmutableArray _lastTokenList = ImmutableArray.Empty; - private ImmutableArray _lastDescriptors = ImmutableArray.Empty; - - /// - /// Set of documents that we have reported an non-empty set of todo comments for. Used so that we don't bother - /// notifying the host about documents with empty-todo lists (the common case). Note: no locking is needed for - /// this set as the incremental analyzer is guaranteed to make all calls sequentially to us. - /// - private readonly HashSet _documentsWithTaskListItems = new(); - - private readonly TaskListListener _listener; - - public TaskListIncrementalAnalyzer(TaskListListener listener) - => _listener = listener; - - public override Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) - { - // Remove the doc id from what we're tracking to prevent unbounded growth in the set. - - // If the doc that is being removed is not in the set of docs we've told the host has todo comments, - // then no need to notify the host at all about it. - if (!_documentsWithTaskListItems.Remove(documentId)) - return Task.CompletedTask; - - // Otherwise, report that there should now be no todo comments for this doc. - return _listener.ReportTaskListItemsAsync(documentId, ImmutableArray.Empty, cancellationToken).AsTask(); - } - - private ImmutableArray GetDescriptors(ImmutableArray tokenList) - { - lock (_gate) - { - if (!tokenList.SequenceEqual(_lastTokenList)) - { - _lastDescriptors = TaskListItemDescriptor.Parse(tokenList); - _lastTokenList = tokenList; - } - - return _lastDescriptors; - } - } - - public override async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) - { - var service = document.GetLanguageService(); - if (service == null) - return; - - var options = await _listener.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var descriptors = GetDescriptors(options.Descriptors); - - // We're out of date. Recompute this info. - var items = await service.GetTaskListItemsAsync( - document, descriptors, cancellationToken).ConfigureAwait(false); - - if (items.IsEmpty) - { - // Remove this doc from the set of docs with todo comments in it. If this was a doc that previously - // had todo comments in it, then fall through and notify the host so it can clear them out. - // Otherwise, bail out as there's no need to inform the host of this. - if (!_documentsWithTaskListItems.Remove(document.Id)) - return; - } - else - { - // Doc has some todo comments, record that, and let the host know. - _documentsWithTaskListItems.Add(document.Id); - } - - // Now inform VS about this new information - await _listener.ReportTaskListItemsAsync(document.Id, items, cancellationToken).ConfigureAwait(false); - } - } -} - -#endif diff --git a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzerProvider.cs b/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzerProvider.cs deleted file mode 100644 index 56d6f48eee4c2..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListIncrementalAnalyzerProvider.cs +++ /dev/null @@ -1,29 +0,0 @@ -#if false - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.TaskList; - -namespace Microsoft.CodeAnalysis.TaskList -{ - /// Note: this is explicitly not exported. We don't want the workspace - /// to automatically load this. Instead, VS waits until it is ready - /// and then calls into the service to tell it to start analyzing the solution. At that point we'll get - /// created and added to the solution crawler. - /// - internal sealed class TaskListIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider - { - private readonly TaskListListener _listener; - - public TaskListIncrementalAnalyzerProvider(TaskListListener listener) - => _listener = listener; - - public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) - => new TaskListIncrementalAnalyzer(_listener); - } -} - -#endif diff --git a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs b/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs deleted file mode 100644 index b98f84db21b58..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/TaskList/TaskListListener.cs +++ /dev/null @@ -1,163 +0,0 @@ -#if false - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Concurrent; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.TaskList; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.TaskList -{ - internal sealed class TaskListListener : ITaskListListener - { - private readonly CancellationToken _disposalToken; - private readonly IGlobalOptionService _globalOptions; - private readonly SolutionServices _services; - private readonly IAsynchronousOperationListener _asyncListener; - private readonly Action, ImmutableArray> _onTaskListItemsUpdated; - private readonly ConcurrentDictionary> _documentToTaskListItems = new(); - - /// - /// Queue where we enqueue the information we get from OOP to process in batch in the future. - /// - private readonly AsyncBatchingWorkQueue<(DocumentId documentId, ImmutableArray items)> _workQueue; - - public TaskListListener( - IGlobalOptionService globalOptions, - SolutionServices services, - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, - Action, ImmutableArray> onTaskListItemsUpdated, - CancellationToken disposalToken) - { - _globalOptions = globalOptions; - _services = services; - _asyncListener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.TaskList); - _onTaskListItemsUpdated = onTaskListItemsUpdated; - _disposalToken = disposalToken; - - _workQueue = new AsyncBatchingWorkQueue<(DocumentId documentId, ImmutableArray items)>( - TimeSpan.FromSeconds(1), - ProcessTaskListItemsAsync, - _asyncListener, - _disposalToken); - } - - public void Start() - { -<<<<<<< HEAD -======= - // If we're in pull-diagnostics mode, then todo-comments will be handled by LSP. - var diagnosticMode = _globalOptions.GetDiagnosticMode(); - if (diagnosticMode == DiagnosticMode.LspPull) - return; - ->>>>>>> upstream/main - var registrationService = _services.GetRequiredService(); - var analyzerProvider = new TaskListIncrementalAnalyzerProvider(this); - - registrationService.AddAnalyzerProvider( - analyzerProvider, - new IncrementalAnalyzerProviderMetadata( - nameof(TaskListIncrementalAnalyzerProvider), - highPriorityForActiveFile: false, - workspaceKinds: WorkspaceKind.Host)); - } - - /// - /// Callback from the OOP service back into us. - /// - public ValueTask ReportTaskListItemsAsync(DocumentId documentId, ImmutableArray items, CancellationToken cancellationToken) - { - try - { - _workQueue.AddWork((documentId, items)); - return ValueTaskFactory.CompletedTask; - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - // report NFW before returning back to the remote process - throw ExceptionUtilities.Unreachable(); - } - } - - /// - /// Callback from the OOP service back into us. - /// - public ValueTask GetOptionsAsync(CancellationToken cancellationToken) - => ValueTaskFactory.FromResult(_globalOptions.GetTaskListOptions()); - - private ValueTask ProcessTaskListItemsAsync( - ImmutableSegmentedList<(DocumentId documentId, ImmutableArray items)> docAndCommentsArray, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - using var _1 = ArrayBuilder<(DocumentId documentId, ImmutableArray items)>.GetInstance(out var filteredArray); - AddFilteredItems(docAndCommentsArray, filteredArray); - - foreach (var (documentId, newItems) in filteredArray) - { - var oldComments = _documentToTaskListItems.TryGetValue(documentId, out var oldBoxedInfos) - ? oldBoxedInfos - : ImmutableArray.Empty; - - // only one thread can be executing ProcessTodoCommentInfosAsync at a time, - // so it's safe to remove/add here. - if (newItems.IsEmpty) - { - _documentToTaskListItems.TryRemove(documentId, out _); - } - else - { - _documentToTaskListItems[documentId] = newItems; - } - - // If we have someone listening for updates, and our new items are different from - // our old ones, then notify them of the change. - _onTaskListItemsUpdated(documentId, oldComments, newItems); - } - - return ValueTaskFactory.CompletedTask; - } - - private static void AddFilteredItems( - ImmutableSegmentedList<(DocumentId documentId, ImmutableArray items)> array, - ArrayBuilder<(DocumentId documentId, ImmutableArray items)> filteredArray) - { - using var _ = PooledHashSet.GetInstance(out var seenDocumentIds); - - // Walk the list of todo comments in reverse, and skip any items for a document once - // we've already seen it once. That way, we're only reporting the most up to date - // information for a document, and we're skipping the stale information. - for (var i = array.Count - 1; i >= 0; i--) - { - var info = array[i]; - if (seenDocumentIds.Add(info.documentId)) - filteredArray.Add(info); - } - } - - public ImmutableArray GetTaskListItems(DocumentId documentId) - { - return _documentToTaskListItems.TryGetValue(documentId, out var values) - ? values - : ImmutableArray.Empty; - } - } -} - -#endif From 4410145c70cb9fa825c1aff7f3213703a99730d5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 12 Feb 2024 15:49:16 -0800 Subject: [PATCH 002/151] fix --- ...gnosticAnalyzerService_IncrementalAnalyzer.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index def96562929ab..f556d7b1affb4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -18,11 +18,7 @@ internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider { public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) { - // We rely on LSP to query us for diagnostics when things have changed and poll us for changes that might - // have happened to the project or closed files outside of VS. - // However, we still need to create the analyzer so that the map contains the analyzer to run when pull diagnostics asks. - _ = _map.GetValue(workspace, _createIncrementalAnalyzer); - return NoOpIncrementalAnalyzer.Instance; + return _map.GetValue(workspace, _createIncrementalAnalyzer); } public void ShutdownAnalyzerFrom(Workspace workspace) @@ -46,14 +42,4 @@ private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspac private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) => Reanalyze(e.Solution.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); } - - internal class NoOpIncrementalAnalyzer : IncrementalAnalyzerBase - { - public static NoOpIncrementalAnalyzer Instance = new(); - - /// - /// Set to a low priority so everything else runs first. - /// - public override int Priority => 5; - } } From ec1df0a34ee079d0250fd6789e1586473b8093c5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 12:38:55 -0800 Subject: [PATCH 003/151] Remove portions of solution crawler --- .../Core/Interactive/InteractiveWorkspace.cs | 4 ++++ .../Core/Preview/AbstractPreviewFactoryService.cs | 2 ++ .../Core/Shared/Preview/PreviewWorkspace.cs | 4 ++++ .../HostSolutionCrawlerWorkspaceEventListener.cs | 4 ++++ .../HostLegacySolutionEventsWorkspaceEventListener.cs | 2 ++ src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs | 2 ++ .../Test/SolutionCrawler/WorkCoordinatorTests.cs | 2 ++ .../Test2/Diagnostics/DiagnosticProviderTests.vb | 6 ++++++ .../Test2/Diagnostics/DiagnosticServiceTests.vb | 2 ++ .../TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs | 8 +++++++- .../LanguageServer/AbstractLanguageServerProtocolTests.cs | 4 ++++ .../API/UnitTestingIncrementalAnalyzerProvider.cs | 4 ++++ .../SolutionCrawler/SolutionCrawlerProgressReporter.cs | 3 +++ .../SolutionCrawler/SolutionCrawlerRegistrationService.cs | 3 +++ .../Portable/SolutionCrawler/SolutionCrawlerService.cs | 3 +++ .../WorkCoordinator.AbstractPriorityProcessor.cs | 4 ++++ .../WorkCoordinator.AsyncDocumentWorkItemQueue.cs | 4 ++++ .../WorkCoordinator.AsyncProjectWorkItemQueue.cs | 3 +++ .../SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs | 4 ++++ .../WorkCoordinator.HighPriorityProcessor.cs | 3 +++ .../WorkCoordinator.IncrementalAnalyzerProcessor.cs | 2 ++ .../WorkCoordinator.LowPriorityProcessor.cs | 3 +++ .../WorkCoordinator.NormalPriorityProcessor.cs | 3 +++ .../WorkCoordinator.SemanticChangeProcessor.cs | 3 +++ .../Core/Portable/SolutionCrawler/WorkCoordinator.cs | 2 ++ .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 2 ++ .../MiscSolutionCrawlerWorkspaceEventListener.cs | 3 +++ src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs | 4 ++++ .../Def/Remote/VisualStudioRemoteHostClientProvider.cs | 6 +++++- .../Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs | 2 ++ .../Core/Test.Next/Services/SolutionServiceTests.cs | 2 ++ .../Impl/Client/RemoteLanguageServiceWorkspace.cs | 8 +++++++- .../Core/Portable/Diagnostics/DiagnosticProvider.cs | 2 ++ .../ISolutionCrawlerRegistrationService.cs | 2 ++ .../NullSolutionCrawlerRegisterationService.cs | 2 ++ src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs | 2 ++ .../DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs | 2 ++ 37 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs index 2348187d602f1..f4881e32c00b9 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs @@ -20,16 +20,20 @@ internal InteractiveWorkspace(HostServices hostServices, IGlobalOptionService gl : base(hostServices, WorkspaceKind.Interactive) { // register work coordinator for this workspace +#if false if (globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { Services.GetRequiredService().Register(this); } +#endif } protected override void Dispose(bool finalize) { // workspace is going away. unregister this workspace from work coordinator +#if false Services.GetRequiredService().Unregister(this, blockingShutdown: true); +#endif base.Dispose(finalize); } diff --git a/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs b/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs index e0b4b55c71216..67b9513cdf9c4 100644 --- a/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs +++ b/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs @@ -701,11 +701,13 @@ private async ValueTask> CreateNewDi rightWorkspace = null; }; +#if false if (_editorOptionsService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { leftWorkspace?.Target.EnableSolutionCrawler(); rightWorkspace?.Target.EnableSolutionCrawler(); } +#endif return CreateDifferenceViewerPreview(diffViewer); } diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs index b1a92e6a3931a..e4347bd04aa40 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs @@ -32,10 +32,12 @@ public PreviewWorkspace(Solution solution) this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.SolutionChanged, oldSolution, newSolution); } +#if false public void EnableSolutionCrawler() { Services.GetRequiredService().Register(this); } +#endif public override bool CanApplyChange(ApplyChangesKind feature) { @@ -107,7 +109,9 @@ protected override void Dispose(bool finalize) { base.Dispose(finalize); +#if false Services.GetRequiredService().Unregister(this); +#endif ClearSolution(); } } diff --git a/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs index 1698d65c1cecf..044b1fa41e581 100644 --- a/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs @@ -19,18 +19,22 @@ internal sealed class HostSolutionCrawlerWorkspaceEventListener(IGlobalOptionSer public void StartListening(Workspace workspace, object? serviceOpt) { +#if false if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { workspace.Services.GetRequiredService().Register(workspace); } +#endif } public void StopListening(Workspace workspace) { +#if false // we do this so that we can stop solution crawler faster and fire some telemetry. // this is to reduce a case where we keep going even when VS is shutting down since we don't know about that var registration = workspace.Services.GetRequiredService(); registration.Unregister(workspace, blockingShutdown: true); +#endif } } } diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs index 66fb52cd1200d..4e8440906c673 100644 --- a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -44,6 +44,7 @@ public HostLegacySolutionEventsWorkspaceEventListener( public void StartListening(Workspace workspace, object? serviceOpt) { +#if false if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { workspace.WorkspaceChanged += OnWorkspaceChanged; @@ -52,6 +53,7 @@ public void StartListening(Workspace workspace, object? serviceOpt) workspace.WorkspaceChanged -= OnWorkspaceChanged; }); } +#endif } private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) diff --git a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs index 62a0ed5e96432..cfd3ccb9b9b86 100644 --- a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs @@ -123,9 +123,11 @@ public void TestPreviewOpenCloseFile() public async Task TestPreviewServices() { using var previewWorkspace = new PreviewWorkspace(EditorTestCompositions.EditorFeatures.GetHostServices()); +#if false var service = previewWorkspace.Services.GetService(); var registrationService = Assert.IsType(service); Assert.False(registrationService.Register(previewWorkspace)); +#endif var persistentService = previewWorkspace.Services.SolutionServices.GetPersistentStorageService(); diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index cb530d727f1b1..6bb5ede2b388f 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false #nullable disable using System; @@ -2159,3 +2160,4 @@ public string Trace() #endif } } +#endif diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index 72c48199dae8e..4d8d6439705af 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -268,8 +268,10 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, language, BackgroundAnalysisScope.OpenFiles) Next +#If False Then Dim registrationService = workspace.Services.GetService(Of ISolutionCrawlerRegistrationService)() registrationService.Register(workspace) +#End If Dim diagnosticProvider = GetDiagnosticProvider(workspace) Dim actualDiagnostics = diagnosticProvider.GetCachedDiagnosticsAsync(workspace, projectId:=Nothing, documentId:=Nothing, @@ -278,7 +280,9 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests includeNonLocalDocumentDiagnostics:=True, CancellationToken.None).Result +#If False Then registrationService.Unregister(workspace) +#End If If diagnostics Is Nothing Then Assert.Equal(0, actualDiagnostics.Length) @@ -305,8 +309,10 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzerService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) ' CollectErrors generates interleaved background and foreground tasks. +#If False Then Dim service = DirectCast(workspace.Services.GetService(Of ISolutionCrawlerRegistrationService)(), SolutionCrawlerRegistrationService) service.GetTestAccessor().WaitUntilCompletion(workspace, SpecializedCollections.SingletonEnumerable(analyzerService.CreateIncrementalAnalyzer(workspace)).WhereNotNull().ToImmutableArray()) +#End If Return analyzerService End Function diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 0edf031f76a4e..a4222f0982cd0 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2502,6 +2502,7 @@ class MyClass End Using End Function +#If False Then Public Sub ReanalysisScopeExcludesMissingDocuments() Dim test = @@ -2518,6 +2519,7 @@ class MyClass Assert.Empty(reanalysisScope.GetDocumentIds(solution)) End Using End Sub +#End If Private NotInheritable Class AnalyzerWithCustomDiagnosticCategory diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index 2ac65be0d5992..3ae49b6e3eb5a 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -51,6 +51,7 @@ public DiagnosticTaggerWrapper( _workspace = workspace; +#if false _registrationService = (SolutionCrawlerRegistrationService)workspace.Services.GetRequiredService(); _registrationService.Register(workspace); @@ -58,6 +59,7 @@ public DiagnosticTaggerWrapper( throw new InvalidOperationException(); AnalyzerService = (DiagnosticAnalyzerService?)_registrationService.GetTestAccessor().AnalyzerProviders.SelectMany(pair => pair.Value).SingleOrDefault(lazyProvider => lazyProvider.Metadata.Name == WellKnownSolutionCrawlerAnalyzers.Diagnostic && lazyProvider.Metadata.HighPriorityForActiveFile)?.Value; +#endif DiagnosticService = (DiagnosticService)workspace.ExportProvider.GetExportedValue(); if (updateSource is object) @@ -96,7 +98,11 @@ public ITaggerProvider TaggerProvider } public void Dispose() - => _registrationService.Unregister(_workspace); + { +#if false + _registrationService.Unregister(_workspace); +#endif + } public async Task WaitForTags() { diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 6684a5a8afce1..6b8982d5b0212 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -397,9 +397,11 @@ internal EditorTestWorkspace CreateWorkspace( workspace.GetService().Register(workspace); +#if false // solution crawler is currently required in order to create incremental analyzer that provides diagnostics var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)workspace.Services.GetRequiredService(); solutionCrawlerRegistrationService.Register(workspace); +#endif return workspace; } @@ -743,8 +745,10 @@ public async ValueTask DisposeAsync() TestWorkspace.GetService().Deregister(TestWorkspace); TestWorkspace.GetService().Deregister(GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()); +#if false var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)TestWorkspace.Services.GetRequiredService(); solutionCrawlerRegistrationService.Unregister(TestWorkspace); +#endif // Some tests will manually call shutdown and exit, so attempting to call this during dispose // will fail as the server's jsonrpc instance will be disposed of. diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingIncrementalAnalyzerProvider.cs index ddb46144c0683..6b3ebb168d6b3 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingIncrementalAnalyzerProvider.cs @@ -38,11 +38,13 @@ public void Reanalyze() public static UnitTestingIncrementalAnalyzerProvider? TryRegister(Workspace workspace, string analyzerName, IUnitTestingIncrementalAnalyzerProviderImplementation provider) { +#if false var solutionCrawlerRegistrationService = workspace.Services.GetService(); if (solutionCrawlerRegistrationService == null) { return null; } +#endif var analyzerProvider = new UnitTestingIncrementalAnalyzerProvider(workspace, provider); @@ -51,7 +53,9 @@ public void Reanalyze() highPriorityForActiveFile: false, [workspace.Kind]); +#if false solutionCrawlerRegistrationService.AddAnalyzerProvider(analyzerProvider, metadata); +#endif return analyzerProvider; } } diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs index 2facffefc757f..5af2888018b7e 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Threading; @@ -108,3 +110,4 @@ public event EventHandler ProgressChanged } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs index de09df6052098..22681d0a969df 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -315,3 +317,4 @@ public Solution GetSolutionToAnalyze() } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs index a4aa8162ef3be..f9007c4a79200 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Generic; using System.Composition; @@ -53,3 +55,4 @@ public ISolutionCrawlerProgressReporter GetProgressReporter(Workspace workspace) } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs index e7004e9572ae5..2a1ef5b457746 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Immutable; using System.Threading; @@ -152,3 +154,5 @@ private void OnNonRoslynBufferTextChanged(object? sender, EventArgs e) } } } + +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs index 1459f3be2cb7d..207cabc548cba 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System.Collections.Generic; using System.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics; @@ -145,3 +147,5 @@ protected override void Dispose_NoLock() } } } + +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs index d4a12b0e2d363..ed1f99ddc6b00 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -96,3 +98,4 @@ protected override void Dispose_NoLock() } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs index 1ac87b679bf81..2173fb0568559 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Generic; using System.Diagnostics; @@ -273,3 +275,5 @@ protected static ProjectId GetBestProjectId_NoLock( } } } + +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs index 90ace94ca558f..e3575758c1d0c 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Immutable; using System.Diagnostics; @@ -235,3 +237,4 @@ public void Shutdown() } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs index 6385aaa337b06..89303412b3909 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -406,3 +407,4 @@ public ImmutableArray GetOrderedAnalyzers(Workspace worksp } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs index 421019888a8d6..151ee8a3fa464 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -234,3 +236,4 @@ internal void WaitUntilCompletion() } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index 4c62cdeb84f52..07368e88e9600 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -571,3 +573,4 @@ internal void WaitUntilCompletion() } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs index ea83b324a9f01..118793274e810 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -446,3 +448,4 @@ private readonly struct Data(ProjectId projectId, bool needDependencyTracking, I } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 982d7541eb909..f7b1d5fc374b5 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -761,3 +762,4 @@ public int GetDocumentCount(Solution solution) } } } +#endif diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 719875f368e88..a570c0fd371e9 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -71,12 +71,14 @@ public DiagnosticIncrementalAnalyzer( private void OnGlobalOptionChanged(object? sender, OptionChangedEventArgs e) { +#if false if (DiagnosticAnalyzerService.IsGlobalOptionAffectingDiagnostics(e.Option) && GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { var service = Workspace.Services.GetService(); service?.Reanalyze(Workspace, this, projectIds: null, documentIds: null, highPriority: false); } +#endif } internal IGlobalOptionService GlobalOptions => AnalyzerService.GlobalOptions; diff --git a/src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs b/src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs index 591b7e7650080..65464927eb34b 100644 --- a/src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs +++ b/src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; using System.Composition; using Microsoft.CodeAnalysis.Host; @@ -32,3 +34,4 @@ public void StopListening(Workspace workspace) => DiagnosticProvider.Disable(workspace); } } +#endif diff --git a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs index 76be36386db05..0d41e7afe397a 100644 --- a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs +++ b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs @@ -49,8 +49,10 @@ public async Task RunAsync(CancellationToken cancellationToken) var workspaceConfigurationService = (AnalyzerRunnerWorkspaceConfigurationService)_workspace.Services.GetRequiredService(); workspaceConfigurationService.Options = new(CacheStorage: usePersistentStorage ? StorageDatabase.SQLite : StorageDatabase.None); +#if false var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)_workspace.Services.GetRequiredService(); solutionCrawlerRegistrationService.Register(_workspace); +#endif if (usePersistentStorage) { @@ -70,7 +72,9 @@ public async Task RunAsync(CancellationToken cancellationToken) incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).SingleOrDefault(provider => provider.Metadata.WorkspaceKinds?.Contains(WorkspaceKind.RemoteWorkspace) ?? false)?.Value; incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).Single(provider => provider.Metadata.WorkspaceKinds is null).Value; var incrementalAnalyzer = incrementalAnalyzerProvider.CreateIncrementalAnalyzer(_workspace); +#if false solutionCrawlerRegistrationService.GetTestAccessor().WaitUntilCompletion(_workspace, ImmutableArray.Create(incrementalAnalyzer)); +#endif } } } diff --git a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs index fd21cfe3e2ecf..e0e2fb4bc0670 100644 --- a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs +++ b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs @@ -119,8 +119,12 @@ private VisualStudioRemoteHostClientProvider( var configuration = (_globalOptions.GetOption(RemoteHostOptionsStorage.OOPCoreClr) ? RemoteProcessConfiguration.Core : 0) | - (_globalOptions.GetOption(RemoteHostOptionsStorage.OOPServerGCFeatureFlag) ? RemoteProcessConfiguration.ServerGC : 0) | + (_globalOptions.GetOption(RemoteHostOptionsStorage.OOPServerGCFeatureFlag) ? RemoteProcessConfiguration.ServerGC : 0); + +#if false + | (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler) ? RemoteProcessConfiguration.EnableSolutionCrawler : 0); +#endif // VS AsyncLazy does not currently support cancellation: var client = await ServiceHubRemoteHostClient.CreateAsync(Services, configuration, _listenerProvider, serviceBroker, _callbackDispatchers, CancellationToken.None).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs index 0fdb3b11313a4..9d12164f5addf 100644 --- a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -369,11 +369,13 @@ internal void OnSolutionBuildCompleted() // user might have built solution before workspace fires its first event yet (which is when solution crawler is initialized) // here we give initializeLazily: false so that solution crawler is fully initialized when we do de-dup live and build errors, // otherwise, we will think none of error we have here belong to live errors since diagnostic service is not initialized yet. +#if false if (_diagnosticService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { var registrationService = (SolutionCrawlerRegistrationService)_workspace.Services.GetRequiredService(); registrationService.EnsureRegistration(_workspace, initializeLazily: false); } +#endif // Mark the status as updated to refresh error list before we invoke 'SyncBuildErrorsAndReportAsync', which can take some time to complete. OnBuildProgressChanged(inProgressState, BuildProgress.Updated); diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 731d3123c5493..4f6e09e392a60 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -329,6 +329,7 @@ await VerifySolutionUpdate(workspace, s => }); } +#if false [Fact] public async Task TestRemoteWorkspaceSolutionCrawler() { @@ -374,6 +375,7 @@ public async Task TestRemoteWorkspaceSolutionCrawler() // check solution update correctly ran solution crawler Assert.True(await testAnalyzerProvider.Analyzer.Called); } +#endif [Fact] public async Task TestRemoteWorkspace() diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index c7b7bdb4e127b..103bf5b816cf9 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -527,11 +527,17 @@ private static void UpdateText(ITextBuffer textBuffer, SourceText text) private void StartSolutionCrawler() { +#if false if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) DiagnosticProvider.Enable(this); +#endif } private void StopSolutionCrawler() - => DiagnosticProvider.Disable(this); + { +#if false + DiagnosticProvider.Disable(this); +#endif + } } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs index 9cd46a3bec736..699a4e3cf3ffa 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false #nullable disable using System; @@ -28,3 +29,4 @@ public static void Disable(Workspace workspace) } } } +#endif diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs b/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs index b7e32630fb5ca..b7a6f51bb0c10 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.SolutionCrawler @@ -22,3 +23,4 @@ internal interface ISolutionCrawlerRegistrationService : IWorkspaceService void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata); } } +#endif diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs b/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs index 12ebc58fec4f8..4b86d19ebe6f3 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false #nullable disable using System; @@ -39,3 +40,4 @@ public void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, Increment } } } +#endif diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs index b4bda851d777b..eded02c0cf074 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs @@ -42,7 +42,9 @@ internal RemoteWorkspace(HostServices hostServices) protected override void Dispose(bool finalize) { base.Dispose(finalize); +#if false Services.GetRequiredService().Unregister(this); +#endif } public AssetProvider CreateAssetProvider(Checksum solutionChecksum, SolutionAssetCache assetCache, IAssetSource assetSource) diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs index b782f8b136f8d..32f151b65da1c 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs @@ -43,7 +43,9 @@ public ValueTask StartSolutionCrawlerAsync(CancellationToken cancellationToken) { // register solution crawler: var workspace = GetWorkspace(); +#if false workspace.Services.GetRequiredService().Register(workspace); +#endif return ValueTaskFactory.CompletedTask; }, cancellationToken); From ff22e7cf44d37b76784b38518f2c306b69f48290 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:06:49 -0800 Subject: [PATCH 004/151] remove solution crawler type --- .../Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs | 2 ++ .../Core/Def/Diagnostics/DiagnosticProgressReporter.cs | 2 ++ .../Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index fbc4d8d24f62b..0c5f1249d66be 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -77,11 +77,13 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) public void Reanalyze(Workspace workspace, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) { +#if false var service = workspace.Services.GetService(); if (service != null && _map.TryGetValue(workspace, out var analyzer)) { service.Reanalyze(workspace, analyzer, projectIds, documentIds, highPriority); } +#endif } public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( diff --git a/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs b/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs index da37475dc76ba..ef0add70c902c 100644 --- a/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs +++ b/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs @@ -96,6 +96,7 @@ public async Task InitializeAsync() { _taskCenterService = await _taskStatusCenterService.GetValueAsync().ConfigureAwait(false); +#if false var crawlerService = _workspace.Services.GetRequiredService(); var reporter = crawlerService.GetProgressReporter(_workspace); @@ -106,6 +107,7 @@ public async Task InitializeAsync() } reporter.ProgressChanged += OnSolutionCrawlerProgressChanged; +#endif } /// diff --git a/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs b/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs index fc1639256673e..17b41bd74ea8c 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs @@ -41,6 +41,7 @@ protected ImmutableArray GetDocumentsWithSameFilePath(Solution solut private void ConnectToSolutionCrawlerService(Workspace workspace) { +#if false var crawlerService = workspace.Services.GetService(); if (crawlerService == null) { @@ -53,6 +54,7 @@ private void ConnectToSolutionCrawlerService(Workspace workspace) // set initial value SolutionCrawlerProgressChanged(reporter.InProgress); +#endif } private void OnSolutionCrawlerProgressChanged(object sender, ProgressData progressData) From ef6b8fa8b209abe058e22bd513ad7718ab09dfff Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:12:54 -0800 Subject: [PATCH 005/151] remove solution crawler type --- .../Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs | 2 ++ .../Core/Impl/CodeModel/ProjectCodeModelFactory.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs index a2cf0772837cc..d2321f688e1d4 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System; namespace Microsoft.CodeAnalysis.SolutionCrawler @@ -16,3 +17,4 @@ internal static class SolutionCrawlerTimeSpan public static readonly TimeSpan PreviewBackOff = TimeSpan.FromMilliseconds(500); } } +#endif diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 00f8208ba67eb..b6f80d5508f2d 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -14,7 +14,9 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; @@ -62,7 +64,7 @@ public ProjectCodeModelFactory( // for the same documents. Once enough time has passed, take the documents that were changed and run // through them, firing their latest events. _documentsToFireEventsFor = new AsyncBatchingWorkQueue( - SolutionCrawlerTimeSpan.AllFilesWorkerBackOff, + DelayTimeSpan.Idle, ProcessNextDocumentBatchAsync, // We only care about unique doc-ids, so pass in this comparer to collapse streams of changes for a // single document down to one notification. From 90f15989095d83f5ef9bcd40cb777fd19c4e3d98 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:15:01 -0800 Subject: [PATCH 006/151] remove solution crawler type --- .../Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs index 5c1c4e80af00a..0bba4e7dacab7 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -294,3 +295,4 @@ public static void LogProcessProjectNotExist(CountLogAggregator logAggre => logAggregator.IncreaseCount(ProjectNotExist); } } +#endif From 65c70ce564f77dbcdfb2703e2a12f910bc7a3a2b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:21:28 -0800 Subject: [PATCH 007/151] remove solution crawler type --- .../Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs b/src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs index 9b58b4ab1d533..14f86d0537a90 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -18,3 +19,4 @@ internal interface IWorkCoordinatorPriorityService : ILanguageService Task IsLowPriorityAsync(Document document, CancellationToken cancellationToken); } } +#endif From 7a1178443b01b2d077c4f2d64991da0b0b16b27b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:26:56 -0800 Subject: [PATCH 008/151] remove solution crawler type --- .../SolutionCrawler/ISolutionCrawlerProgressReporter.cs | 3 +++ .../Core/Def/Diagnostics/DiagnosticProgressReporter.cs | 3 ++- src/VisualStudio/Core/Def/RoslynPackage.cs | 2 ++ .../Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs b/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs index ccb96c0b67cb4..1b21c16631018 100644 --- a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs +++ b/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false + using System; namespace Microsoft.CodeAnalysis.SolutionCrawler @@ -45,3 +47,4 @@ internal enum ProgressStatus Stopped } } +#endif diff --git a/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs b/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs index ef0add70c902c..9d5e3130d4df3 100644 --- a/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs +++ b/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System; using System.ComponentModel.Composition; using System.Threading; @@ -200,4 +201,4 @@ private void StopTaskCenter() } } } - +#endif diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 3dd44fe8b03b7..2c4904ec79c73 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -223,7 +223,9 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation // we need to load it as early as possible since we can have errors from // package from each language very early +#if false await this.ComponentModel.GetService().InitializeAsync().ConfigureAwait(false); +#endif await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs b/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs index 17b41bd74ea8c..c07c38748bb8d 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs @@ -57,6 +57,7 @@ private void ConnectToSolutionCrawlerService(Workspace workspace) #endif } +#if false private void OnSolutionCrawlerProgressChanged(object sender, ProgressData progressData) { switch (progressData.Status) @@ -69,6 +70,7 @@ private void OnSolutionCrawlerProgressChanged(object sender, ProgressData progre break; } } +#endif private void SolutionCrawlerProgressChanged(bool running) { From 207b22c6689bc44011f2b218e13de348c4ff6595 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:35:21 -0800 Subject: [PATCH 009/151] remove solution crawler type --- .../Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs | 2 ++ .../Core/Test.Next/Services/SolutionServiceTests.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs b/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs index 64ccc790c0bb4..834b6c7ca593d 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; @@ -70,3 +71,4 @@ public virtual void Shutdown() } } } +#endif diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 4f6e09e392a60..b0f986f141e68 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -974,6 +974,7 @@ private static async Task GetAssetProviderAsync(Workspace workspa return new AssetProvider(sessionId, storage, assetSource, remoteWorkspace.Services.GetService()); } +#if false private class TestAnalyzerProvider : IIncrementalAnalyzerProvider { public readonly TestAnalyzer Analyzer = new TestAnalyzer(); @@ -1001,5 +1002,6 @@ public void Reset() } } } +#endif } } From b79e96c3aac5758a585beecf0207b7e98d8491b4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:42:21 -0800 Subject: [PATCH 010/151] remove solution crawler type --- src/Features/Core/Portable/SolutionCrawler/Extensions.cs | 2 ++ .../Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Features/Core/Portable/SolutionCrawler/Extensions.cs b/src/Features/Core/Portable/SolutionCrawler/Extensions.cs index 2f2b17bdf8dfe..cb25d9dbd400e 100644 --- a/src/Features/Core/Portable/SolutionCrawler/Extensions.cs +++ b/src/Features/Core/Portable/SolutionCrawler/Extensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System; using System.Text; @@ -24,3 +25,4 @@ public static string DecodeBase64(this string data) } } } +#endif diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs index c9243e66d286e..d30ac8356d05d 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if false using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -154,3 +155,4 @@ public override string ToString() } } } +#endif From fcdff66cce5b7b0f8e7260e7678d6d599c91e9e3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 13:44:10 -0800 Subject: [PATCH 011/151] remove solution crawler type --- .../TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index 3ae49b6e3eb5a..b66dbf8438c27 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -25,7 +25,9 @@ internal class DiagnosticTaggerWrapper : IDisposable { private readonly EditorTestWorkspace _workspace; public readonly DiagnosticAnalyzerService? AnalyzerService; +#if false private readonly SolutionCrawlerRegistrationService _registrationService; +#endif public readonly DiagnosticService DiagnosticService; private readonly IThreadingContext _threadingContext; private readonly IAsynchronousOperationListenerProvider _listenerProvider; From 439280288b84e29515e91f869063fb3f27612ce4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 14:34:37 -0800 Subject: [PATCH 012/151] REmove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 3 --- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 5 ----- 2 files changed, 8 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 34b0c088aed71..f1b48238fbaf5 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -237,9 +237,6 @@ private async Task TextDocumentCloseAsync(TextDocument document, CancellationTok } } - public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) - => TextDocumentResetAsync(document, cancellationToken); - public Task NonSourceDocumentResetAsync(TextDocument document, CancellationToken cancellationToken) => TextDocumentResetAsync(document, cancellationToken); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 851595e85919c..7f6c4a5ce9da4 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -18,11 +18,6 @@ internal interface IIncrementalAnalyzer Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); - /// - /// Resets all the document state cached by the analyzer. - /// - Task DocumentResetAsync(Document document, CancellationToken cancellationToken); - Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); From 825de4e8126a034629d31ff767ef58ee2adbe326 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 14:35:54 -0800 Subject: [PATCH 013/151] REmove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 3 --- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 5 ----- 2 files changed, 8 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index f1b48238fbaf5..58b7fe1503abe 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -237,9 +237,6 @@ private async Task TextDocumentCloseAsync(TextDocument document, CancellationTok } } - public Task NonSourceDocumentResetAsync(TextDocument document, CancellationToken cancellationToken) - => TextDocumentResetAsync(document, cancellationToken); - private Task TextDocumentResetAsync(TextDocument document, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_DocumentReset, GetResetLogMessage, document, cancellationToken)) diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 7f6c4a5ce9da4..7203c2d3a4e35 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -28,11 +28,6 @@ internal interface IIncrementalAnalyzer Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); - /// - /// Resets all the document state cached by the analyzer. - /// - Task NonSourceDocumentResetAsync(TextDocument textDocument, CancellationToken cancellationToken); - Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); void LogAnalyzerCountSummary(); From cd8f428b561fbc53c42ea250446d6465c25c898f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 15 Feb 2024 14:37:31 -0800 Subject: [PATCH 014/151] REmove unused functionality --- .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 5 ----- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 1 - 2 files changed, 6 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index a570c0fd371e9..3c7dc5b2d9860 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -237,11 +237,6 @@ private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary _telemetry.ReportAndClear(_correlationId); - /// - /// The highest priority (lowest value) amongst all incremental analyzers (others have priority 1). - /// - public int Priority => 0; - internal IEnumerable GetAnalyzersTestOnly(Project project) => _stateManager.GetOrCreateStateSets(project).Select(s => s.Analyzer); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 7203c2d3a4e35..b5e899ad65421 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -31,7 +31,6 @@ internal interface IIncrementalAnalyzer Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); void LogAnalyzerCountSummary(); - int Priority { get; } void Shutdown(); } } From e6dff022d3c0c9e166a4c9989cc3ac76be8ce025 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 09:48:01 -0800 Subject: [PATCH 015/151] Remove unused functionality --- .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 5 ----- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 1 - 2 files changed, 6 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 3c7dc5b2d9860..42589d01d60d7 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -10,12 +10,10 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -234,9 +232,6 @@ private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary _telemetry.ReportAndClear(_correlationId); - internal IEnumerable GetAnalyzersTestOnly(Project project) => _stateManager.GetOrCreateStateSets(project).Select(s => s.Analyzer); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index b5e899ad65421..5009051e03d88 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -30,7 +30,6 @@ internal interface IIncrementalAnalyzer Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); - void LogAnalyzerCountSummary(); void Shutdown(); } } From a8682eea6ca42f7e9ba227729052db53a176e757 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 09:58:04 -0800 Subject: [PATCH 016/151] Remove unused functionality --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 4 ++++ .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 7 +++++++ .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 3 +++ 3 files changed, 14 insertions(+) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 0eb8393260292..cf073dd1838dd 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -695,7 +695,9 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool case BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics: case BackgroundAnalysisScope.OpenFiles: workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); +#if false await incrementalAnalyzer.AnalyzeNonSourceDocumentAsync(firstAdditionalDocument, InvocationReasons.SyntaxChanged, CancellationToken.None); +#endif break; case BackgroundAnalysisScope.FullSolution: @@ -1351,7 +1353,9 @@ private static async Task RunAllAnalysisAsync(IIncrementalAnalyzer analyzer, Tex } else { +#if false await analyzer.AnalyzeNonSourceDocumentAsync(textDocument, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); +#endif } await analyzer.AnalyzeProjectAsync(textDocument.Project, semanticsChanged: true, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 42589d01d60d7..527e3c20862d6 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -10,10 +10,12 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; +using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -232,6 +234,11 @@ private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary _telemetry.ReportAndClear(_correlationId); +#endif + internal IEnumerable GetAnalyzersTestOnly(Project project) => _stateManager.GetOrCreateStateSets(project).Select(s => s.Analyzer); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 5009051e03d88..5d11a30904f2c 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -28,7 +28,10 @@ internal interface IIncrementalAnalyzer Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); +#if false Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); + void LogAnalyzerCountSummary(); +#endif void Shutdown(); } From 075969a0c839eb6302f6b352b2ccca4e8264a621 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 09:58:38 -0800 Subject: [PATCH 017/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 4 ++++ .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 58b7fe1503abe..00d472ee34bfd 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -195,8 +195,10 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) => TextDocumentOpenAsync(document, cancellationToken); +#if false public Task NonSourceDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) => TextDocumentOpenAsync(document, cancellationToken); +#endif private async Task TextDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) { @@ -216,8 +218,10 @@ private async Task TextDocumentOpenAsync(TextDocument document, CancellationToke public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) => TextDocumentCloseAsync(document, cancellationToken); +#if false public Task NonSourceDocumentCloseAsync(TextDocument document, CancellationToken cancellationToken) => TextDocumentCloseAsync(document, cancellationToken); +#endif private async Task TextDocumentCloseAsync(TextDocument document, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 5d11a30904f2c..3badf1e60fcc3 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -25,10 +25,10 @@ internal interface IIncrementalAnalyzer Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); +#if false Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); -#if false Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); void LogAnalyzerCountSummary(); #endif From 08000579615ca6141b685bb8b73fad8c018e4d84 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:01:39 -0800 Subject: [PATCH 018/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 4 ++++ .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 00d472ee34bfd..d3dcec0075eae 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -305,6 +305,7 @@ public async Task ActiveDocumentSwitchedAsync(TextDocument document, Cancellatio await AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false); } +#if false public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_RemoveDocument, GetRemoveLogMessage, documentId, CancellationToken.None)) @@ -326,6 +327,7 @@ public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancell return Task.CompletedTask; } +#endif private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerable stateSets) { @@ -345,6 +347,7 @@ private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerab }); } +#if false public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellation) { using (Logger.LogBlock(FunctionId.Diagnostics_RemoveProject, GetRemoveLogMessage, projectId, CancellationToken.None)) @@ -376,6 +379,7 @@ public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellati return Task.CompletedTask; } +#endif public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 3badf1e60fcc3..14f76f943786b 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -22,10 +22,10 @@ internal interface IIncrementalAnalyzer Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); +#if false Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); -#if false Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); From 211d963cc4edf160b861fef4567ed95aea92b15f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:06:17 -0800 Subject: [PATCH 019/151] Remove unused functionality --- .../DiagnosticAnalyzerServiceTests.cs | 20 +++++++++++++++++-- .../Diagnostics/DiagnosticServiceTests.vb | 2 ++ ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 ++ .../SolutionCrawler/IIncrementalAnalyzer.cs | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index cf073dd1838dd..1f1e28f3f4859 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -526,8 +526,9 @@ public async Task TestHostAnalyzerErrorNotLeaking() }; var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); +#if false await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); - +#endif await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.True(called); @@ -635,8 +636,9 @@ private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace }; var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(project.Solution.Workspace); +#if false await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); - +#endif await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.Equal(expectAnalyzerExecuted, called); @@ -701,7 +703,9 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool break; case BackgroundAnalysisScope.FullSolution: +#if false await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); +#endif break; default: @@ -823,7 +827,9 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS break; case BackgroundAnalysisScope.FullSolution: +#if false await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); +#endif break; default: @@ -967,7 +973,9 @@ void M() break; case BackgroundAnalysisScope.FullSolution: +#if false await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); +#endif break; } @@ -1296,9 +1304,15 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); if (analyzeProject) + { +#if false await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); +#endif + } else + { await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); + } await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); @@ -1358,7 +1372,9 @@ private static async Task RunAllAnalysisAsync(IIncrementalAnalyzer analyzer, Tex #endif } +#if false await analyzer.AnalyzeProjectAsync(textDocument.Project, semanticsChanged: true, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); +#endif } private class Analyzer : DiagnosticAnalyzer diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index a4222f0982cd0..6bdf2db136ce1 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2198,8 +2198,10 @@ class MyClass Assert.Equal(1, descriptors.Length) Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id) +#If False Then ' Force project analysis incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged:=True, reasons:=InvocationReasons.Empty, cancellationToken:=CancellationToken.None).Wait() +#End If ' Get cached project diagnostics. Dim diagnostics = Await diagnosticService.GetCachedDiagnosticsAsync(workspace, project.Id, documentId:=Nothing, diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index d3dcec0075eae..6fb390ab7f3da 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -135,10 +135,12 @@ void OnAnalysisException() } } +#if false public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) { await AnalyzeProjectAsync(project, forceAnalyzerRun: false, cancellationToken).ConfigureAwait(false); } +#endif public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) => AnalyzeProjectAsync(project, forceAnalyzerRun: true, cancellationToken); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 14f76f943786b..35f74383dc599 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -20,9 +20,9 @@ internal interface IIncrementalAnalyzer Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); +#if false Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); -#if false Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); From ea39b88bc3affb338489664769a9f170da3195a2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:09:41 -0800 Subject: [PATCH 020/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 6fb390ab7f3da..c8046e8d98908 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -381,7 +381,6 @@ public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellati return Task.CompletedTask; } -#endif public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) { @@ -390,6 +389,7 @@ public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancel return Task.CompletedTask; } +#endif /// /// Return list of to be used for full solution analysis. diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 35f74383dc599..dbbdea58ce460 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -12,8 +12,6 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal interface IIncrementalAnalyzer { - Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); - Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); @@ -21,6 +19,7 @@ internal interface IIncrementalAnalyzer Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); #if false + Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); From e758b1591ef9495cc132130d42cb5d444900a545 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:11:44 -0800 Subject: [PATCH 021/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index c8046e8d98908..573a3df539507 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -286,6 +286,7 @@ private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocum RaiseDiagnosticsRemovedForDocument(document.Id, stateSets); } +#if false public async Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) { // Retrigger analysis of newly active document to always get up-to-date diagnostics. @@ -307,7 +308,6 @@ public async Task ActiveDocumentSwitchedAsync(TextDocument document, Cancellatio await AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false); } -#if false public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Diagnostics_RemoveDocument, GetRemoveLogMessage, documentId, CancellationToken.None)) diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index dbbdea58ce460..6da7f9b67873f 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -14,11 +14,11 @@ internal interface IIncrementalAnalyzer { Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); - Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); #if false + Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); From 9b1cd8dfd43e32bd6beb49597ec19281ff42640c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:13:49 -0800 Subject: [PATCH 022/151] Remove unused functionality --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 4 ++++ .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 1f1e28f3f4859..8352f332fe542 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -235,7 +235,9 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab // open document workspace.OpenDocument(document.Id); +#if false await analyzer.DocumentOpenAsync(document, CancellationToken.None).ConfigureAwait(false); +#endif // run analysis await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); @@ -336,7 +338,9 @@ public async Task TestOpenFileOnlyAnalyzerDiagnostics() // open document workspace.OpenDocument(document.Id); +#if false await analyzer.DocumentOpenAsync(document, CancellationToken.None).ConfigureAwait(false); +#endif // cause analysis await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 573a3df539507..6a8452d3f2cb4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -194,10 +194,10 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C } } +#if false public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) => TextDocumentOpenAsync(document, cancellationToken); -#if false public Task NonSourceDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) => TextDocumentOpenAsync(document, cancellationToken); #endif diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 6da7f9b67873f..6361c559f7d72 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -12,12 +12,12 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal interface IIncrementalAnalyzer { - Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); #if false + Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); From a1b8045af24b254430f1916503790be53d31d959 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:16:33 -0800 Subject: [PATCH 023/151] Remove unused functionality --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 2 ++ .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 3 +-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 8352f332fe542..24d7ec60741f6 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -347,7 +347,9 @@ public async Task TestOpenFileOnlyAnalyzerDiagnostics() // close document workspace.CloseDocument(document.Id); +#if false await analyzer.DocumentCloseAsync(document, CancellationToken.None).ConfigureAwait(false); +#endif await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 6a8452d3f2cb4..ef83d20dc130c 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -217,10 +217,10 @@ private async Task TextDocumentOpenAsync(TextDocument document, CancellationToke } } +#if false public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) => TextDocumentCloseAsync(document, cancellationToken); -#if false public Task NonSourceDocumentCloseAsync(TextDocument document, CancellationToken cancellationToken) => TextDocumentCloseAsync(document, cancellationToken); #endif diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 6361c559f7d72..52cc25f839c8b 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -12,11 +12,10 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal interface IIncrementalAnalyzer { - Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); - Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); #if false + Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); From 28cd397afa594344b834bd7f0111ad763daf421e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:18:15 -0800 Subject: [PATCH 024/151] Remove unused functionality --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 6 ++++++ .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 3 ++- .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 24d7ec60741f6..213be77396b0d 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -1071,7 +1071,9 @@ void M() { if (actionKind == AnalyzerRegisterActionKind.SyntaxTree) { +#if false await incrementalAnalyzer.AnalyzeSyntaxAsync(document, InvocationReasons.SyntaxChanged, analyzer.CancellationToken); +#endif } else { @@ -1090,7 +1092,9 @@ void M() // Then invoke analysis without cancellation token, and verify non-cancelled diagnostic. if (actionKind == AnalyzerRegisterActionKind.SyntaxTree) { +#if false await incrementalAnalyzer.AnalyzeSyntaxAsync(document, InvocationReasons.SyntaxChanged, CancellationToken.None); +#endif } else { @@ -1368,7 +1372,9 @@ private static async Task RunAllAnalysisAsync(IIncrementalAnalyzer analyzer, Tex { if (textDocument is Document document) { +#if false await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); +#endif await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); } else diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index ef83d20dc130c..f9796e3e639df 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -22,9 +22,10 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { +#if false public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(document, AnalysisKind.Syntax, cancellationToken); - +#endif public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 52cc25f839c8b..afc55db55e427 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -12,9 +12,9 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal interface IIncrementalAnalyzer { - Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); #if false + Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); From 88d00a9c6038ba10ff405db21697087d6a7877d0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:20:59 -0800 Subject: [PATCH 025/151] Remove unused functionality --- .../Test/CodeFixes/CodeFixServiceTests.cs | 5 +++++ .../DiagnosticAnalyzerServiceTests.cs | 20 ++++++++++++++----- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- .../SolutionCrawler/IIncrementalAnalyzer.cs | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 7e7ab65d495e5..17da44ca6b8ee 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1073,7 +1073,10 @@ void M() // Trigger background analysis to ensure analyzer diagnostics are computed and cached. // We enable full solution analysis so the 'AnalyzeDocumentAsync' doesn't skip analysis based on whether the document is active/open. workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); + +#if false await diagnosticIncrementalAnalyzer.AnalyzeDocumentAsync(sourceDocument, bodyOpt: null!, InvocationReasons.DocumentChanged, CancellationToken.None); +#endif await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnosticIncrementalAnalyzer); // Compute and apply code edit @@ -1106,7 +1109,9 @@ void M() if (testWithCachedDiagnostics) { // Trigger background analysis to ensure analyzer diagnostics are computed and cached. +#if false await diagnosticIncrementalAnalyzer.AnalyzeDocumentAsync(sourceDocument, bodyOpt: null!, InvocationReasons.DocumentChanged, CancellationToken.None); +#endif await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: !expectedNoFixes, testSpan, diagnosticIncrementalAnalyzer); } diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 213be77396b0d..34bf9cfc72396 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -824,12 +824,16 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS workspace.OpenDocument(document.Id); var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetService(); documentTrackingService.SetActiveDocument(document.Id); +#if false await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); +#endif break; case BackgroundAnalysisScope.OpenFiles: workspace.OpenDocument(document.Id); +#if false await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); +#endif break; case BackgroundAnalysisScope.FullSolution: @@ -966,7 +970,9 @@ void M() var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetRequiredService(); documentTrackingService.SetActiveDocument(document.Id); +#if false await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); +#endif break; case BackgroundAnalysisScope.OpenFiles: @@ -975,7 +981,9 @@ void M() else workspace.OpenDocument(document.Id); +#if false await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); +#endif break; case BackgroundAnalysisScope.FullSolution: @@ -1077,7 +1085,9 @@ void M() } else { +#if false await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, analyzer.CancellationToken); +#endif } throw ExceptionUtilities.Unreachable(); @@ -1098,7 +1108,9 @@ void M() } else { +#if false await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); +#endif } await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); @@ -1321,7 +1333,9 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, } else { +#if false await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); +#endif } await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); @@ -1370,21 +1384,17 @@ private static (bool, bool) CompilerAnalyzerResultSetter(bool syntax, bool seman private static async Task RunAllAnalysisAsync(IIncrementalAnalyzer analyzer, TextDocument textDocument) { +#if false if (textDocument is Document document) { -#if false await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); -#endif await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); } else { -#if false await analyzer.AnalyzeNonSourceDocumentAsync(textDocument, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); -#endif } -#if false await analyzer.AnalyzeProjectAsync(textDocument.Project, semanticsChanged: true, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); #endif } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index f9796e3e639df..456b6aab75449 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -25,9 +25,9 @@ internal partial class DiagnosticIncrementalAnalyzer #if false public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(document, AnalysisKind.Syntax, cancellationToken); -#endif public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken); +#endif public Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(textDocument, AnalysisKind.Syntax, cancellationToken); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index afc55db55e427..089e7e8459491 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -12,8 +12,8 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal interface IIncrementalAnalyzer { - Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); #if false + Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); From ab895d9e62deb77da193bd36436f6494fccce3b8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:23:12 -0800 Subject: [PATCH 026/151] Remove unused functionality --- .../DiagnosticAnalyzerService_IncrementalAnalyzer.cs | 2 ++ .../Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index f556d7b1affb4..1329738e8d1ae 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -21,6 +21,7 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) return _map.GetValue(workspace, _createIncrementalAnalyzer); } +#if false public void ShutdownAnalyzerFrom(Workspace workspace) { // this should be only called once analyzer associated with the workspace is done. @@ -29,6 +30,7 @@ public void ShutdownAnalyzerFrom(Workspace workspace) analyzer.Shutdown(); } } +#endif [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace) diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 089e7e8459491..1447ae9e3f209 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -29,8 +29,7 @@ internal interface IIncrementalAnalyzer Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); void LogAnalyzerCountSummary(); -#endif - void Shutdown(); +#endif } } From bbd3f8f6550ee572672e88d9763886f5a09451d6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:24:50 -0800 Subject: [PATCH 027/151] Remove unused functionality --- .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 527e3c20862d6..06eaced4f156e 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -66,7 +66,9 @@ public DiagnosticIncrementalAnalyzer( _diagnosticAnalyzerRunner = new InProcOrRemoteHostAnalyzerRunner(analyzerInfoCache, analyzerService.Listener); +#if false GlobalOptions.AddOptionChangedHandler(this, OnGlobalOptionChanged); +#endif } private void OnGlobalOptionChanged(object? sender, OptionChangedEventArgs e) @@ -105,11 +107,12 @@ private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerRe ClearAllDiagnostics(stateSets, project.Id); } +#if false public void Shutdown() { GlobalOptions.RemoveOptionChangedHandler(this, OnGlobalOptionChanged); - var stateSets = _stateManager.GetAllStateSets(); + var stateSets = _stateManager.GetAllStateSets(); AnalyzerService.RaiseBulkDiagnosticsUpdated(raiseEvents => { @@ -131,6 +134,7 @@ public void Shutdown() raiseEvents(argsBuilder.ToImmutableAndClear()); }); } +#endif private void ClearAllDiagnostics(ImmutableArray stateSets, ProjectId projectId) { From b07bb3bed7c6f5c24501e6ae2f3c833141640803 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:25:15 -0800 Subject: [PATCH 028/151] Remove unused functionality --- .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 06eaced4f156e..f43ebdaf4723a 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -71,17 +71,17 @@ public DiagnosticIncrementalAnalyzer( #endif } +#if false private void OnGlobalOptionChanged(object? sender, OptionChangedEventArgs e) { -#if false if (DiagnosticAnalyzerService.IsGlobalOptionAffectingDiagnostics(e.Option) && GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { var service = Workspace.Services.GetService(); service?.Reanalyze(Workspace, this, projectIds: null, documentIds: null, highPriority: false); } -#endif } +#endif internal IGlobalOptionService GlobalOptions => AnalyzerService.GlobalOptions; internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; From 788606938ae478801f69bc26dbf909cc5215aeb2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:26:53 -0800 Subject: [PATCH 029/151] Remove unused functionality --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 2 ++ .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 34bf9cfc72396..8abc226b2c32a 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -463,7 +463,9 @@ public void TestHostAnalyzerOrdering() var service = Assert.IsType(exportProvider.GetExportedValue()); var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); +#if false var analyzers = incrementalAnalyzer.GetAnalyzersTestOnly(project).ToArray(); +#endif AssertEx.Equal(new[] { diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index f43ebdaf4723a..3c350ea63e2ec 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -241,10 +241,10 @@ private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary _telemetry.ReportAndClear(_correlationId); -#endif internal IEnumerable GetAnalyzersTestOnly(Project project) => _stateManager.GetOrCreateStateSets(project).Select(s => s.Analyzer); +#endif private static string GetDocumentLogMessage(string title, TextDocument document, DiagnosticAnalyzer analyzer) => $"{title}: ({document.Id}, {document.Project.Id}), ({analyzer})"; From 90a7a40c21a958eff18e976c215585c3c4ce9d43 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:30:52 -0800 Subject: [PATCH 030/151] Remove unused functionality --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 2 +- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 8abc226b2c32a..ee91f880e0352 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -465,7 +465,6 @@ public void TestHostAnalyzerOrdering() var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); #if false var analyzers = incrementalAnalyzer.GetAnalyzersTestOnly(project).ToArray(); -#endif AssertEx.Equal(new[] { @@ -479,6 +478,7 @@ public void TestHostAnalyzerOrdering() typeof(Priority15Analyzer), typeof(Priority20Analyzer) }, analyzers.Select(a => a.GetType())); +#endif } [Fact] diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 456b6aab75449..125c8d899ec29 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -27,10 +27,9 @@ public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, Can => AnalyzeDocumentForKindAsync(document, AnalysisKind.Syntax, cancellationToken); public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken); -#endif - public Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(textDocument, AnalysisKind.Syntax, cancellationToken); +#endif private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKind kind, CancellationToken cancellationToken) { From 679cba647c1d45b7d8efedf5706ce36f9fe2a673 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:31:22 -0800 Subject: [PATCH 031/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 125c8d899ec29..abfe4785e547d 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -29,7 +29,6 @@ public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, Invocati => AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken); public Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) => AnalyzeDocumentForKindAsync(textDocument, AnalysisKind.Syntax, cancellationToken); -#endif private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKind kind, CancellationToken cancellationToken) { @@ -135,7 +134,6 @@ void OnAnalysisException() } } -#if false public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) { await AnalyzeProjectAsync(project, forceAnalyzerRun: false, cancellationToken).ConfigureAwait(false); From 19e44f3ce30e3268432635506c7c8c61f2b024c3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:32:51 -0800 Subject: [PATCH 032/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index abfe4785e547d..2bf5035320dec 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -198,7 +198,6 @@ public Task DocumentOpenAsync(Document document, CancellationToken cancellationT public Task NonSourceDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) => TextDocumentOpenAsync(document, cancellationToken); -#endif private async Task TextDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) { @@ -215,7 +214,6 @@ private async Task TextDocumentOpenAsync(TextDocument document, CancellationToke } } -#if false public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) => TextDocumentCloseAsync(document, cancellationToken); From 879505f213cf233ced0fa966b415d01606ce2876 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:33:22 -0800 Subject: [PATCH 033/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 2bf5035320dec..1b0a0ad2b8226 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -219,7 +219,6 @@ public Task DocumentCloseAsync(Document document, CancellationToken cancellation public Task NonSourceDocumentCloseAsync(TextDocument document, CancellationToken cancellationToken) => TextDocumentCloseAsync(document, cancellationToken); -#endif private async Task TextDocumentCloseAsync(TextDocument document, CancellationToken cancellationToken) { @@ -238,6 +237,7 @@ private async Task TextDocumentCloseAsync(TextDocument document, CancellationTok RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); } } +#endif private Task TextDocumentResetAsync(TextDocument document, CancellationToken cancellationToken) { From 3c2ad1aa87af46b56872df1badfbf75d95285b04 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:33:37 -0800 Subject: [PATCH 034/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 1b0a0ad2b8226..26970c606651c 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -237,7 +237,6 @@ private async Task TextDocumentCloseAsync(TextDocument document, CancellationTok RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); } } -#endif private Task TextDocumentResetAsync(TextDocument document, CancellationToken cancellationToken) { @@ -257,6 +256,7 @@ private Task TextDocumentResetAsync(TextDocument document, CancellationToken can return Task.CompletedTask; } +#endif private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocument document, IEnumerable stateSets, bool documentHadDiagnostics) { From 7a753d47370fd793911dd3747af795a8ff2b2755 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:34:25 -0800 Subject: [PATCH 035/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 26970c606651c..56e012817dd1a 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -256,7 +256,6 @@ private Task TextDocumentResetAsync(TextDocument document, CancellationToken can return Task.CompletedTask; } -#endif private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocument document, IEnumerable stateSets, bool documentHadDiagnostics) { @@ -282,7 +281,6 @@ private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocum RaiseDiagnosticsRemovedForDocument(document.Id, stateSets); } -#if false public async Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) { // Retrigger analysis of newly active document to always get up-to-date diagnostics. From 4235f614b81fe835afb5b66e2e6919ee5678579f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:34:43 -0800 Subject: [PATCH 036/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 56e012817dd1a..cbd21cbb1fdca 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -323,7 +323,6 @@ public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancell return Task.CompletedTask; } -#endif private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerable stateSets) { @@ -343,7 +342,6 @@ private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerab }); } -#if false public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellation) { using (Logger.LogBlock(FunctionId.Diagnostics_RemoveProject, GetRemoveLogMessage, projectId, CancellationToken.None)) From e4a0ec789e63656a6b05ec05f17b22fe696b8bd6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:35:40 -0800 Subject: [PATCH 037/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index cbd21cbb1fdca..fae8979e12aa4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -531,10 +531,12 @@ private void RaiseProjectDiagnosticsIfNeeded( }); } +#if false private void AddDocumentDiagnosticsArgsIfNeeded( ref TemporaryArray builder, TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray items) => AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, [], items); +#endif private void AddDocumentDiagnosticsArgsIfNeeded( ref TemporaryArray builder, From 2e702fd0fbe1b748c823e57bcb1524bd07912ae1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:36:47 -0800 Subject: [PATCH 038/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index fae8979e12aa4..9108fbd852b53 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -536,7 +536,6 @@ private void AddDocumentDiagnosticsArgsIfNeeded( ref TemporaryArray builder, TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray items) => AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, [], items); -#endif private void AddDocumentDiagnosticsArgsIfNeeded( ref TemporaryArray builder, @@ -544,6 +543,7 @@ private void AddDocumentDiagnosticsArgsIfNeeded( { AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, oldItems, newItems, forceUpdate: false); } +#endif private void AddDocumentDiagnosticsArgsIfNeeded( ref TemporaryArray builder, From ffdaa85cbd96b34850076b3625bdb3ceb4392ad7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:38:16 -0800 Subject: [PATCH 039/151] Remove unused functionality --- .../EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index a9a17bc284977..856da6122cccd 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -62,10 +62,12 @@ public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray< public DocumentAnalysisData ToPersistData() => new(Version, LineCount, Items); +#if false public bool FromCache { get { return OldItems.IsDefault; } } +#endif } /// From 0f74f3ae6baf5d1e43d6d517bad9cb2ded645747 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:38:46 -0800 Subject: [PATCH 040/151] Remove unused functionality --- .../EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 856da6122cccd..49478b045cc30 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -59,10 +59,10 @@ public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray< OldItems = oldItems; } +#if false public DocumentAnalysisData ToPersistData() => new(Version, LineCount, Items); -#if false public bool FromCache { get { return OldItems.IsDefault; } From 63183a7199fe09be253f956e27bf30e3191420d1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:39:34 -0800 Subject: [PATCH 041/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index c137c22c1bdce..30d48bcfc2541 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -60,6 +60,7 @@ internal partial class DiagnosticIncrementalAnalyzer private static Task CreateCompilationWithAnalyzersAsync(Project project, IdeAnalyzerOptions ideOptions, IEnumerable stateSets, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, ideOptions, stateSets.Select(s => s.Analyzer), includeSuppressedDiagnostics, cancellationToken); +#if false private void ClearCompilationsWithAnalyzersCache(Project project) => _projectCompilationsWithAnalyzers.Remove(project); @@ -75,6 +76,7 @@ private void ClearCompilationsWithAnalyzersCache() _projectCompilationsWithAnalyzers.Clear(); #endif } +#endif [Conditional("DEBUG")] private static void AssertAnalyzers(CompilationWithAnalyzers? compilation, IEnumerable stateSets) From 198dfeba0cc76f625c8678bc026aecc3b6d13302 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:39:56 -0800 Subject: [PATCH 042/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 30d48bcfc2541..cc5027c6ecee6 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { +#if false /// /// Return CompilationWithAnalyzer for given project with given stateSets /// @@ -56,6 +57,7 @@ internal partial class DiagnosticIncrementalAnalyzer return compilationWithAnalyzers; } +#endif private static Task CreateCompilationWithAnalyzersAsync(Project project, IdeAnalyzerOptions ideOptions, IEnumerable stateSets, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, ideOptions, stateSets.Select(s => s.Analyzer), includeSuppressedDiagnostics, cancellationToken); From 9b013fcae747fee0a1aa6e1e88e52a69fd723ff9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:40:20 -0800 Subject: [PATCH 043/151] Remove unused functionality --- .../DiagnosticIncrementalAnalyzer.CompilationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index cc5027c6ecee6..8c4addbc97d78 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -78,7 +78,6 @@ private void ClearCompilationsWithAnalyzersCache() _projectCompilationsWithAnalyzers.Clear(); #endif } -#endif [Conditional("DEBUG")] private static void AssertAnalyzers(CompilationWithAnalyzers? compilation, IEnumerable stateSets) @@ -92,5 +91,6 @@ private static void AssertAnalyzers(CompilationWithAnalyzers? compilation, IEnum // make sure analyzers are same. Contract.ThrowIfFalse(compilation.Analyzers.SetEquals(stateSets.Select(s => s.Analyzer).Where(a => !a.IsWorkspaceDiagnosticAnalyzer()))); } +#endif } } From 55dc44bf0ee214aba8473ba19f0c35960622868c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 16 Feb 2024 10:51:50 -0800 Subject: [PATCH 044/151] Remove unused functionality --- .../AbstractDocumentDifferenceService.cs | 16 ++++++++-------- .../IDocumentDifferenceService.cs | 5 +++++ ...ntalAnalyzer.IncrementalMemberEditAnalyzer.cs | 4 ++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs index f8a65a1d9d0ba..b6173b7629859 100644 --- a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceService { - public async Task GetDifferenceAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) + public async Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { try { @@ -23,7 +23,7 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS if (syntaxFactsService == null) { // somehow, we can't get the service. without it, there is nothing we can do. - return new DocumentDifferenceResult(InvocationReasons.DocumentChanged); + return null; } // this is based on the implementation detail where opened documents use strong references // to tree and text rather than recoverable versions. @@ -31,7 +31,7 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS !newDocument.TryGetText(out var newText)) { // no cheap way to determine top level changes. assumes top level has changed - return new DocumentDifferenceResult(InvocationReasons.DocumentChanged); + return null; } // quick check whether two tree versions are same if (oldDocument.TryGetSyntaxVersion(out var oldVersion) && @@ -58,7 +58,7 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS if (!incrementalParsingCandidate) { // no cheap way to determine top level changes. assumes top level has changed - return new DocumentDifferenceResult(InvocationReasons.DocumentChanged); + return null; } // explicitly parse them @@ -81,18 +81,18 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS { if (oldTopLevelChangeVersion.Equals(newTopLevelChangeVersion)) { - return new DocumentDifferenceResult(InvocationReasons.SyntaxChanged, GetChangedMember(syntaxFactsService, oldRoot, newRoot, range)); + return GetChangedMember(syntaxFactsService, oldRoot, newRoot, range); } - return new DocumentDifferenceResult(InvocationReasons.DocumentChanged, GetBestGuessChangedMember(syntaxFactsService, oldRoot, newRoot, range)); + return GetBestGuessChangedMember(syntaxFactsService, oldRoot, newRoot, range); } if (oldTopLevelChangeVersion.Equals(newTopLevelChangeVersion)) { - return new DocumentDifferenceResult(InvocationReasons.SyntaxChanged); + return null; } - return new DocumentDifferenceResult(InvocationReasons.DocumentChanged); + return null; } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { diff --git a/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs b/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs index b4a320683cd14..440a3c4e435af 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs @@ -8,14 +8,19 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { +#if false internal class DocumentDifferenceResult(InvocationReasons changeType, SyntaxNode? changedMember = null) { public InvocationReasons ChangeType { get; } = changeType; public SyntaxNode? ChangedMember { get; } = changedMember; } +#endif internal interface IDocumentDifferenceService : ILanguageService { +#if false Task GetDifferenceAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); +#endif + Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 128cf10e4622f..fd15d29e0bff6 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -210,8 +210,8 @@ async Task ExecuteAnalyzersAsync( } var documentDifferenceService = document.GetRequiredLanguageService(); - var differenceResult = await documentDifferenceService.GetDifferenceAsync(lastDocument, document, cancellationToken).ConfigureAwait(false); - if (differenceResult?.ChangedMember is not { } changedMember) + var changedMember = await documentDifferenceService.GetChangedMemberAsync(lastDocument, document, cancellationToken).ConfigureAwait(false); + if (changedMember is null) { return null; } From 8a22cec0533b58590a5f9c6e267b9cee3642d8d2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:17:24 -0800 Subject: [PATCH 045/151] Make file scoped --- ...sticAnalyzerService_IncrementalAnalyzer.cs | 81 +++++++++---------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 213a7601f0fbc..cb687af42e157 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -9,55 +9,54 @@ using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics +namespace Microsoft.CodeAnalysis.Diagnostics; + +[ExportIncrementalAnalyzerProvider( + name: WellKnownSolutionCrawlerAnalyzers.Diagnostic, + workspaceKinds: [WorkspaceKind.Host, WorkspaceKind.Interactive], + highPriorityForActiveFile: true)] +internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider { - [ExportIncrementalAnalyzerProvider( - name: WellKnownSolutionCrawlerAnalyzers.Diagnostic, - workspaceKinds: [WorkspaceKind.Host, WorkspaceKind.Interactive], - highPriorityForActiveFile: true)] - internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider + public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) { - public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) - { - var analyzer = _map.GetValue(workspace, _createIncrementalAnalyzer); - // We rely on LSP to query us for diagnostics when things have changed and poll us for changes that might - // have happened to the project or closed files outside of VS. However, we still need to create the analyzer - // so that the map contains the analyzer to run when pull diagnostics asks. - // - // Temporary code. To keep tests running which test legacy behavior, we allow tests to configure this - // behavior. This code will be removed when we remove the legacy behavior entirely. - return GlobalOptions.GetOption(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag) ? NoOpIncrementalAnalyzer.Instance : analyzer; - } + var analyzer = _map.GetValue(workspace, _createIncrementalAnalyzer); + // We rely on LSP to query us for diagnostics when things have changed and poll us for changes that might + // have happened to the project or closed files outside of VS. However, we still need to create the analyzer + // so that the map contains the analyzer to run when pull diagnostics asks. + // + // Temporary code. To keep tests running which test legacy behavior, we allow tests to configure this + // behavior. This code will be removed when we remove the legacy behavior entirely. + return GlobalOptions.GetOption(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag) ? NoOpIncrementalAnalyzer.Instance : analyzer; + } - public void ShutdownAnalyzerFrom(Workspace workspace) + public void ShutdownAnalyzerFrom(Workspace workspace) + { + // this should be only called once analyzer associated with the workspace is done. + if (_map.TryGetValue(workspace, out var analyzer)) { - // this should be only called once analyzer associated with the workspace is done. - if (_map.TryGetValue(workspace, out var analyzer)) - { - analyzer.Shutdown(); - } + analyzer.Shutdown(); } - - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] - private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace) - { - // subscribe to active context changed event for new workspace - workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - - return new DiagnosticIncrementalAnalyzer(this, CorrelationIdFactory.GetNextId(), workspace, AnalyzerInfoCache); - } - - private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) - => Reanalyze(e.Solution.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); } - internal class NoOpIncrementalAnalyzer : IncrementalAnalyzerBase + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace) { - public static NoOpIncrementalAnalyzer Instance = new(); + // subscribe to active context changed event for new workspace + workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - /// - /// Set to a low priority so everything else runs first. - /// - public override int Priority => 5; + return new DiagnosticIncrementalAnalyzer(this, CorrelationIdFactory.GetNextId(), workspace, AnalyzerInfoCache); } + + private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) + => Reanalyze(e.Solution.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); +} + +internal class NoOpIncrementalAnalyzer : IncrementalAnalyzerBase +{ + public static NoOpIncrementalAnalyzer Instance = new(); + + /// + /// Set to a low priority so everything else runs first. + /// + public override int Priority => 5; } From 6ee97bd957aacf0886755f2e28b0ef69de930658 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:18:03 -0800 Subject: [PATCH 046/151] Make file scoped --- .../VSTypeScriptDiagnosticService.cs | 83 +- .../VSTypeScriptInProcLanguageClient.cs | 95 +- .../AlwaysActivateInProcLanguageClient.cs | 195 +- .../LiveShareInProcLanguageClient.cs | 99 +- .../Diagnostics/DiagnosticsUpdatedArgs.cs | 89 +- ...sticAnalyzerService_IncrementalAnalyzer.cs | 51 +- .../VisualStudioWorkspaceImpl.cs | 2305 ++++++++--------- .../EventListener/WellKnownEventListeners.cs | 15 +- 8 files changed, 1462 insertions(+), 1470 deletions(-) diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs index 4ba448afea45b..3e02d0fa9a118 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs @@ -12,59 +12,58 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; -namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; + +[Export(typeof(IVSTypeScriptDiagnosticService)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class VSTypeScriptDiagnosticService(IDiagnosticService service) : IVSTypeScriptDiagnosticService { - [Export(typeof(IVSTypeScriptDiagnosticService)), Shared] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - internal sealed class VSTypeScriptDiagnosticService(IDiagnosticService service) : IVSTypeScriptDiagnosticService + private readonly IDiagnosticService _service = service; + + public async Task> GetPushDiagnosticsAsync(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) { - private readonly IDiagnosticService _service = service; + var result = await _service.GetDiagnosticsAsync(workspace, projectId, documentId, id, includeSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); + return result.SelectAsArray(data => new VSTypeScriptDiagnosticData(data)); + } - public async Task> GetPushDiagnosticsAsync(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) - { - var result = await _service.GetDiagnosticsAsync(workspace, projectId, documentId, id, includeSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); - return result.SelectAsArray(data => new VSTypeScriptDiagnosticData(data)); - } + [Obsolete] + public IDisposable RegisterDiagnosticsUpdatedEventHandler(Action action) + => new EventHandlerWrapper(_service, action); - [Obsolete] - public IDisposable RegisterDiagnosticsUpdatedEventHandler(Action action) - => new EventHandlerWrapper(_service, action); + public IDisposable RegisterDiagnosticsUpdatedEventHandler(Action> action) + => new EventHandlerWrapper(_service, action); - public IDisposable RegisterDiagnosticsUpdatedEventHandler(Action> action) - => new EventHandlerWrapper(_service, action); + private sealed class EventHandlerWrapper : IDisposable + { + private readonly IDiagnosticService _service; + private readonly EventHandler> _handler; - private sealed class EventHandlerWrapper : IDisposable + [Obsolete] + internal EventHandlerWrapper(IDiagnosticService service, Action action) { - private readonly IDiagnosticService _service; - private readonly EventHandler> _handler; - - [Obsolete] - internal EventHandlerWrapper(IDiagnosticService service, Action action) + _service = service; + _handler = (sender, argsCollection) => { - _service = service; - _handler = (sender, argsCollection) => - { - foreach (var args in argsCollection) - action(new VSTypeScriptDiagnosticsUpdatedArgsWrapper(args)); - }; - _service.DiagnosticsUpdated += _handler; - } + foreach (var args in argsCollection) + action(new VSTypeScriptDiagnosticsUpdatedArgsWrapper(args)); + }; + _service.DiagnosticsUpdated += _handler; + } - internal EventHandlerWrapper(IDiagnosticService service, Action> action) + internal EventHandlerWrapper(IDiagnosticService service, Action> action) + { + _service = service; + _handler = (sender, argsCollection) => { - _service = service; - _handler = (sender, argsCollection) => - { - action(ImmutableArray.CreateRange(argsCollection, static args => new VSTypeScriptDiagnosticsUpdatedArgsWrapper(args))); - }; - _service.DiagnosticsUpdated += _handler; - } + action(ImmutableArray.CreateRange(argsCollection, static args => new VSTypeScriptDiagnosticsUpdatedArgsWrapper(args))); + }; + _service.DiagnosticsUpdated += _handler; + } - public void Dispose() - { - _service.DiagnosticsUpdated -= _handler; - } + public void Dispose() + { + _service.DiagnosticsUpdated -= _handler; } } } diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs index b9c085807986a..78d641cebbfc2 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs @@ -20,64 +20,63 @@ using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; + +/// +/// Language client to handle TS LSP requests. +/// Allows us to move features to LSP without being blocked by TS as well +/// as ensures that TS LSP features use correct solution snapshots. +/// +[ContentType(ContentTypeNames.TypeScriptContentTypeName)] +[ContentType(ContentTypeNames.JavaScriptContentTypeName)] +[Export(typeof(ILanguageClient))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] +internal class VSTypeScriptInProcLanguageClient( + [Import(AllowDefault = true)] IVSTypeScriptCapabilitiesProvider? typeScriptCapabilitiesProvider, + VSTypeScriptLspServiceProvider lspServiceProvider, + IGlobalOptionService globalOptions, + ILspServiceLoggerFactory lspLoggerFactory, + IThreadingContext threadingContext, + ExportProvider exportProvider) : AbstractInProcLanguageClient(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext, exportProvider) { - /// - /// Language client to handle TS LSP requests. - /// Allows us to move features to LSP without being blocked by TS as well - /// as ensures that TS LSP features use correct solution snapshots. - /// - [ContentType(ContentTypeNames.TypeScriptContentTypeName)] - [ContentType(ContentTypeNames.JavaScriptContentTypeName)] - [Export(typeof(ILanguageClient))] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] - internal class VSTypeScriptInProcLanguageClient( - [Import(AllowDefault = true)] IVSTypeScriptCapabilitiesProvider? typeScriptCapabilitiesProvider, - VSTypeScriptLspServiceProvider lspServiceProvider, - IGlobalOptionService globalOptions, - ILspServiceLoggerFactory lspLoggerFactory, - IThreadingContext threadingContext, - ExportProvider exportProvider) : AbstractInProcLanguageClient(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext, exportProvider) - { - private readonly IVSTypeScriptCapabilitiesProvider? _typeScriptCapabilitiesProvider = typeScriptCapabilitiesProvider; + private readonly IVSTypeScriptCapabilitiesProvider? _typeScriptCapabilitiesProvider = typeScriptCapabilitiesProvider; - protected override ImmutableArray SupportedLanguages => [InternalLanguageNames.TypeScript]; + protected override ImmutableArray SupportedLanguages => [InternalLanguageNames.TypeScript]; - public override ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) - { - var serverCapabilities = GetTypeScriptServerCapabilities(clientCapabilities); + public override ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) + { + var serverCapabilities = GetTypeScriptServerCapabilities(clientCapabilities); - serverCapabilities.TextDocumentSync = new TextDocumentSyncOptions - { - Change = TextDocumentSyncKind.Incremental, - OpenClose = true, - }; + serverCapabilities.TextDocumentSync = new TextDocumentSyncOptions + { + Change = TextDocumentSyncKind.Incremental, + OpenClose = true, + }; - serverCapabilities.ProjectContextProvider = true; + serverCapabilities.ProjectContextProvider = true; - serverCapabilities.SupportsDiagnosticRequests = true; - return serverCapabilities; - } + serverCapabilities.SupportsDiagnosticRequests = true; + return serverCapabilities; + } - public override bool ShowNotificationOnInitializeFailed => true; + public override bool ShowNotificationOnInitializeFailed => true; - public override WellKnownLspServerKinds ServerKind => WellKnownLspServerKinds.RoslynTypeScriptLspServer; + public override WellKnownLspServerKinds ServerKind => WellKnownLspServerKinds.RoslynTypeScriptLspServer; - private VSInternalServerCapabilities GetTypeScriptServerCapabilities(ClientCapabilities clientCapabilities) + private VSInternalServerCapabilities GetTypeScriptServerCapabilities(ClientCapabilities clientCapabilities) + { + if (_typeScriptCapabilitiesProvider != null) + { + var serializedClientCapabilities = JsonConvert.SerializeObject(clientCapabilities); + var serializedServerCapabilities = _typeScriptCapabilitiesProvider.GetServerCapabilities(serializedClientCapabilities); + var typeScriptServerCapabilities = JsonConvert.DeserializeObject(serializedServerCapabilities); + Contract.ThrowIfNull(typeScriptServerCapabilities); + return typeScriptServerCapabilities; + } + else { - if (_typeScriptCapabilitiesProvider != null) - { - var serializedClientCapabilities = JsonConvert.SerializeObject(clientCapabilities); - var serializedServerCapabilities = _typeScriptCapabilitiesProvider.GetServerCapabilities(serializedClientCapabilities); - var typeScriptServerCapabilities = JsonConvert.DeserializeObject(serializedServerCapabilities); - Contract.ThrowIfNull(typeScriptServerCapabilities); - return typeScriptServerCapabilities; - } - else - { - return new VSInternalServerCapabilities(); - } + return new VSInternalServerCapabilities(); } } } diff --git a/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs index 663b04af895ff..6a8ca3f59fe2a 100644 --- a/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/AlwaysActivateInProcLanguageClient.cs @@ -19,118 +19,117 @@ using Microsoft.VisualStudio.Utilities; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient +namespace Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient; + +/// +/// Language client responsible for handling C# / VB / F# LSP requests in any scenario (both local and codespaces). +/// This powers "LSP only" features (e.g. cntrl+Q code search) that do not use traditional editor APIs. +/// It is always activated whenever roslyn is activated. +/// +[ContentType(ContentTypeNames.CSharpContentType)] +[ContentType(ContentTypeNames.VisualBasicContentType)] +[ContentType(ContentTypeNames.FSharpContentType)] +[Export(typeof(ILanguageClient))] +[Export(typeof(AlwaysActivateInProcLanguageClient))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] +internal class AlwaysActivateInProcLanguageClient( + CSharpVisualBasicLspServiceProvider lspServiceProvider, + IGlobalOptionService globalOptions, + ExperimentalCapabilitiesProvider defaultCapabilitiesProvider, + ILspServiceLoggerFactory lspLoggerFactory, + IThreadingContext threadingContext, + ExportProvider exportProvider, + [ImportMany] IEnumerable> buildOnlyDiagnostics) : AbstractInProcLanguageClient(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext, exportProvider) { - /// - /// Language client responsible for handling C# / VB / F# LSP requests in any scenario (both local and codespaces). - /// This powers "LSP only" features (e.g. cntrl+Q code search) that do not use traditional editor APIs. - /// It is always activated whenever roslyn is activated. - /// - [ContentType(ContentTypeNames.CSharpContentType)] - [ContentType(ContentTypeNames.VisualBasicContentType)] - [ContentType(ContentTypeNames.FSharpContentType)] - [Export(typeof(ILanguageClient))] - [Export(typeof(AlwaysActivateInProcLanguageClient))] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] - internal class AlwaysActivateInProcLanguageClient( - CSharpVisualBasicLspServiceProvider lspServiceProvider, - IGlobalOptionService globalOptions, - ExperimentalCapabilitiesProvider defaultCapabilitiesProvider, - ILspServiceLoggerFactory lspLoggerFactory, - IThreadingContext threadingContext, - ExportProvider exportProvider, - [ImportMany] IEnumerable> buildOnlyDiagnostics) : AbstractInProcLanguageClient(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext, exportProvider) - { - private readonly ExperimentalCapabilitiesProvider _experimentalCapabilitiesProvider = defaultCapabilitiesProvider; - private readonly IEnumerable> _buildOnlyDiagnostics = buildOnlyDiagnostics; + private readonly ExperimentalCapabilitiesProvider _experimentalCapabilitiesProvider = defaultCapabilitiesProvider; + private readonly IEnumerable> _buildOnlyDiagnostics = buildOnlyDiagnostics; - protected override ImmutableArray SupportedLanguages => ProtocolConstants.RoslynLspLanguages; + protected override ImmutableArray SupportedLanguages => ProtocolConstants.RoslynLspLanguages; - public override ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) - { - // If the LSP editor feature flag is enabled advertise support for LSP features here so they are available locally and remote. - var isLspEditorEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspEditorFeatureFlag); + public override ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) + { + // If the LSP editor feature flag is enabled advertise support for LSP features here so they are available locally and remote. + var isLspEditorEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspEditorFeatureFlag); - var serverCapabilities = isLspEditorEnabled - ? (VSInternalServerCapabilities)_experimentalCapabilitiesProvider.GetCapabilities(clientCapabilities) - : new VSInternalServerCapabilities() + var serverCapabilities = isLspEditorEnabled + ? (VSInternalServerCapabilities)_experimentalCapabilitiesProvider.GetCapabilities(clientCapabilities) + : new VSInternalServerCapabilities() + { + // Even if the flag is off, we want to include text sync capabilities. + TextDocumentSync = new TextDocumentSyncOptions { - // Even if the flag is off, we want to include text sync capabilities. - TextDocumentSync = new TextDocumentSyncOptions - { - Change = TextDocumentSyncKind.Incremental, - OpenClose = true, - }, - }; + Change = TextDocumentSyncKind.Incremental, + OpenClose = true, + }, + }; - serverCapabilities.ProjectContextProvider = true; - serverCapabilities.BreakableRangeProvider = true; + serverCapabilities.ProjectContextProvider = true; + serverCapabilities.BreakableRangeProvider = true; - serverCapabilities.SupportsDiagnosticRequests = true; + serverCapabilities.SupportsDiagnosticRequests = true; - serverCapabilities.DiagnosticProvider ??= new(); - serverCapabilities.DiagnosticProvider = serverCapabilities.DiagnosticProvider with - { - SupportsMultipleContextsDiagnostics = true, - DiagnosticKinds = - [ - // Support a specialized requests dedicated to task-list items. This way the client can ask just - // for these, independently of other diagnostics. They can also throttle themselves to not ask if - // the task list would not be visible. - new(PullDiagnosticCategories.Task), - // Dedicated request for workspace-diagnostics only. We will only respond to these if FSA is on. - new(PullDiagnosticCategories.WorkspaceDocumentsAndProject), - // Fine-grained diagnostics requests. Importantly, this separates out syntactic vs semantic - // requests, allowing the former to quickly reach the user without blocking on the latter. In a - // similar vein, compiler diagnostics are explicitly distinct from analyzer-diagnostics, allowing - // the former to appear as soon as possible as they are much more critical for the user and should - // not be delayed by a slow analyzer. - new(PullDiagnosticCategories.DocumentCompilerSyntax), - new(PullDiagnosticCategories.DocumentCompilerSemantic), - new(PullDiagnosticCategories.DocumentAnalyzerSyntax), - new(PullDiagnosticCategories.DocumentAnalyzerSemantic), - ], - BuildOnlyDiagnosticIds = _buildOnlyDiagnostics - .SelectMany(lazy => lazy.Metadata.BuildOnlyDiagnostics) - .Distinct() - .ToArray(), - }; + serverCapabilities.DiagnosticProvider ??= new(); + serverCapabilities.DiagnosticProvider = serverCapabilities.DiagnosticProvider with + { + SupportsMultipleContextsDiagnostics = true, + DiagnosticKinds = + [ + // Support a specialized requests dedicated to task-list items. This way the client can ask just + // for these, independently of other diagnostics. They can also throttle themselves to not ask if + // the task list would not be visible. + new(PullDiagnosticCategories.Task), + // Dedicated request for workspace-diagnostics only. We will only respond to these if FSA is on. + new(PullDiagnosticCategories.WorkspaceDocumentsAndProject), + // Fine-grained diagnostics requests. Importantly, this separates out syntactic vs semantic + // requests, allowing the former to quickly reach the user without blocking on the latter. In a + // similar vein, compiler diagnostics are explicitly distinct from analyzer-diagnostics, allowing + // the former to appear as soon as possible as they are much more critical for the user and should + // not be delayed by a slow analyzer. + new(PullDiagnosticCategories.DocumentCompilerSyntax), + new(PullDiagnosticCategories.DocumentCompilerSemantic), + new(PullDiagnosticCategories.DocumentAnalyzerSyntax), + new(PullDiagnosticCategories.DocumentAnalyzerSemantic), + ], + BuildOnlyDiagnosticIds = _buildOnlyDiagnostics + .SelectMany(lazy => lazy.Metadata.BuildOnlyDiagnostics) + .Distinct() + .ToArray(), + }; - // This capability is always enabled as we provide cntrl+Q VS search only via LSP in ever scenario. - serverCapabilities.WorkspaceSymbolProvider = true; - // This capability prevents NavigateTo (cntrl+,) from using LSP symbol search when the server also supports WorkspaceSymbolProvider. - // Since WorkspaceSymbolProvider=true always to allow cntrl+Q VS search to function, we set DisableGoToWorkspaceSymbols=true - // when not running the experimental LSP editor. This ensures NavigateTo uses the existing editor APIs. - // However, when the experimental LSP editor is enabled we want LSP to power NavigateTo, so we set DisableGoToWorkspaceSymbols=false. - serverCapabilities.DisableGoToWorkspaceSymbols = !isLspEditorEnabled; + // This capability is always enabled as we provide cntrl+Q VS search only via LSP in ever scenario. + serverCapabilities.WorkspaceSymbolProvider = true; + // This capability prevents NavigateTo (cntrl+,) from using LSP symbol search when the server also supports WorkspaceSymbolProvider. + // Since WorkspaceSymbolProvider=true always to allow cntrl+Q VS search to function, we set DisableGoToWorkspaceSymbols=true + // when not running the experimental LSP editor. This ensures NavigateTo uses the existing editor APIs. + // However, when the experimental LSP editor is enabled we want LSP to power NavigateTo, so we set DisableGoToWorkspaceSymbols=false. + serverCapabilities.DisableGoToWorkspaceSymbols = !isLspEditorEnabled; - var isLspSemanticTokensEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspSemanticTokensFeatureFlag); - if (isLspSemanticTokensEnabled) + var isLspSemanticTokensEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspSemanticTokensFeatureFlag); + if (isLspSemanticTokensEnabled) + { + // Using only range handling has shown to be more performant than using a combination of full/edits/range handling, + // especially for larger files. With range handling, we only need to compute tokens for whatever is in view, while + // with full/edits handling we need to compute tokens for the entire file and then potentially run a diff between + // the old and new tokens. + serverCapabilities.SemanticTokensOptions = new SemanticTokensOptions { - // Using only range handling has shown to be more performant than using a combination of full/edits/range handling, - // especially for larger files. With range handling, we only need to compute tokens for whatever is in view, while - // with full/edits handling we need to compute tokens for the entire file and then potentially run a diff between - // the old and new tokens. - serverCapabilities.SemanticTokensOptions = new SemanticTokensOptions + Full = false, + Range = true, + Legend = new SemanticTokensLegend { - Full = false, - Range = true, - Legend = new SemanticTokensLegend - { - TokenTypes = SemanticTokensSchema.GetSchema(clientCapabilities.HasVisualStudioLspCapability()).AllTokenTypes.ToArray(), - TokenModifiers = SemanticTokensSchema.TokenModifiers - } - }; - } - - serverCapabilities.SpellCheckingProvider = true; - - return serverCapabilities; + TokenTypes = SemanticTokensSchema.GetSchema(clientCapabilities.HasVisualStudioLspCapability()).AllTokenTypes.ToArray(), + TokenModifiers = SemanticTokensSchema.TokenModifiers + } + }; } - public override bool ShowNotificationOnInitializeFailed => true; + serverCapabilities.SpellCheckingProvider = true; - public override WellKnownLspServerKinds ServerKind => WellKnownLspServerKinds.AlwaysActiveVSLspServer; + return serverCapabilities; } + + public override bool ShowNotificationOnInitializeFailed => true; + + public override WellKnownLspServerKinds ServerKind => WellKnownLspServerKinds.AlwaysActiveVSLspServer; } diff --git a/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs index 5c5daf4772ee8..51518baabd66a 100644 --- a/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/LiveShareInProcLanguageClient.cs @@ -15,65 +15,64 @@ using Microsoft.VisualStudio.Utilities; using Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient +namespace Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient; + +// The C# and VB ILanguageClient should not activate on the host. When LiveShare mirrors the C# ILC to the guest, +// they will not copy the DisableUserExperience attribute, so guests will still use the C# ILC. +[DisableUserExperience(true)] +[ContentType(ContentTypeNames.CSharpContentType)] +[ContentType(ContentTypeNames.VisualBasicContentType)] +[Export(typeof(ILanguageClient))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] +internal class LiveShareInProcLanguageClient( + CSharpVisualBasicLspServiceProvider lspServiceProvider, + IGlobalOptionService globalOptions, + ExperimentalCapabilitiesProvider experimentalCapabilitiesProvider, + ILspServiceLoggerFactory lspLoggerFactory, + IThreadingContext threadingContext, + ExportProvider exportProvider) : AbstractInProcLanguageClient(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext, exportProvider) { - // The C# and VB ILanguageClient should not activate on the host. When LiveShare mirrors the C# ILC to the guest, - // they will not copy the DisableUserExperience attribute, so guests will still use the C# ILC. - [DisableUserExperience(true)] - [ContentType(ContentTypeNames.CSharpContentType)] - [ContentType(ContentTypeNames.VisualBasicContentType)] - [Export(typeof(ILanguageClient))] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, true)] - internal class LiveShareInProcLanguageClient( - CSharpVisualBasicLspServiceProvider lspServiceProvider, - IGlobalOptionService globalOptions, - ExperimentalCapabilitiesProvider experimentalCapabilitiesProvider, - ILspServiceLoggerFactory lspLoggerFactory, - IThreadingContext threadingContext, - ExportProvider exportProvider) : AbstractInProcLanguageClient(lspServiceProvider, globalOptions, lspLoggerFactory, threadingContext, exportProvider) - { - private readonly ExperimentalCapabilitiesProvider _experimentalCapabilitiesProvider = experimentalCapabilitiesProvider; + private readonly ExperimentalCapabilitiesProvider _experimentalCapabilitiesProvider = experimentalCapabilitiesProvider; - protected override ImmutableArray SupportedLanguages => ProtocolConstants.RoslynLspLanguages; + protected override ImmutableArray SupportedLanguages => ProtocolConstants.RoslynLspLanguages; - public override ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) - { - var isLspEditorEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspEditorFeatureFlag); + public override ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) + { + var isLspEditorEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspEditorFeatureFlag); - // If the preview feature flag to turn on the LSP editor in local scenarios is on, advertise no capabilities for this Live Share - // LSP server as LSP requests will be serviced by the AlwaysActiveInProcLanguageClient in both local and remote scenarios. - if (isLspEditorEnabled) + // If the preview feature flag to turn on the LSP editor in local scenarios is on, advertise no capabilities for this Live Share + // LSP server as LSP requests will be serviced by the AlwaysActiveInProcLanguageClient in both local and remote scenarios. + if (isLspEditorEnabled) + { + return new VSServerCapabilities { - return new VSServerCapabilities + TextDocumentSync = new TextDocumentSyncOptions { - TextDocumentSync = new TextDocumentSyncOptions - { - OpenClose = false, - Change = TextDocumentSyncKind.None, - } - }; - } - - var defaultCapabilities = _experimentalCapabilitiesProvider.GetCapabilities(clientCapabilities); + OpenClose = false, + Change = TextDocumentSyncKind.None, + } + }; + } - // If the LSP semantic tokens feature flag is enabled, advertise no semantic tokens capabilities for this Live Share - // LSP server as LSP semantic tokens requests will be serviced by the AlwaysActiveInProcLanguageClient in both local and - // remote scenarios. - var isLspSemanticTokenEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspSemanticTokensFeatureFlag); - if (isLspSemanticTokenEnabled) - { - defaultCapabilities.SemanticTokensOptions = null; - } + var defaultCapabilities = _experimentalCapabilitiesProvider.GetCapabilities(clientCapabilities); - return defaultCapabilities; + // If the LSP semantic tokens feature flag is enabled, advertise no semantic tokens capabilities for this Live Share + // LSP server as LSP semantic tokens requests will be serviced by the AlwaysActiveInProcLanguageClient in both local and + // remote scenarios. + var isLspSemanticTokenEnabled = GlobalOptions.GetOption(LspOptionsStorage.LspSemanticTokensFeatureFlag); + if (isLspSemanticTokenEnabled) + { + defaultCapabilities.SemanticTokensOptions = null; } - /// - /// Failures are catastrophic as liveshare guests will not have language features without this server. - /// - public override bool ShowNotificationOnInitializeFailed => true; - - public override WellKnownLspServerKinds ServerKind => WellKnownLspServerKinds.LiveShareLspServer; + return defaultCapabilities; } + + /// + /// Failures are catastrophic as liveshare guests will not have language features without this server. + /// + public override bool ShowNotificationOnInitializeFailed => true; + + public override WellKnownLspServerKinds ServerKind => WellKnownLspServerKinds.LiveShareLspServer; } diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs index c1a7026f2834b..3315d31522eff 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs @@ -7,52 +7,51 @@ using System.Linq; using Microsoft.CodeAnalysis.Common; -namespace Microsoft.CodeAnalysis.Diagnostics +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal sealed class DiagnosticsUpdatedArgs : UpdatedEventArgs { - internal sealed class DiagnosticsUpdatedArgs : UpdatedEventArgs + public DiagnosticsUpdatedKind Kind { get; } + public Solution? Solution { get; } + + public readonly ImmutableArray Diagnostics; + + private DiagnosticsUpdatedArgs( + object id, + Workspace workspace, + Solution? solution, + ProjectId? projectId, + DocumentId? documentId, + ImmutableArray diagnostics, + DiagnosticsUpdatedKind kind) + : base(id, workspace, projectId, documentId) + { + Debug.Assert(diagnostics.All(d => d.ProjectId == projectId && d.DocumentId == documentId)); + Debug.Assert(kind != DiagnosticsUpdatedKind.DiagnosticsRemoved || diagnostics.IsEmpty); + + Solution = solution; + Kind = kind; + Diagnostics = diagnostics; + } + + public static DiagnosticsUpdatedArgs DiagnosticsCreated( + object id, + Workspace workspace, + Solution? solution, + ProjectId? projectId, + DocumentId? documentId, + ImmutableArray diagnostics) + { + return new DiagnosticsUpdatedArgs(id, workspace, solution, projectId, documentId, diagnostics, DiagnosticsUpdatedKind.DiagnosticsCreated); + } + + public static DiagnosticsUpdatedArgs DiagnosticsRemoved( + object id, + Workspace workspace, + Solution? solution, + ProjectId? projectId, + DocumentId? documentId) { - public DiagnosticsUpdatedKind Kind { get; } - public Solution? Solution { get; } - - public readonly ImmutableArray Diagnostics; - - private DiagnosticsUpdatedArgs( - object id, - Workspace workspace, - Solution? solution, - ProjectId? projectId, - DocumentId? documentId, - ImmutableArray diagnostics, - DiagnosticsUpdatedKind kind) - : base(id, workspace, projectId, documentId) - { - Debug.Assert(diagnostics.All(d => d.ProjectId == projectId && d.DocumentId == documentId)); - Debug.Assert(kind != DiagnosticsUpdatedKind.DiagnosticsRemoved || diagnostics.IsEmpty); - - Solution = solution; - Kind = kind; - Diagnostics = diagnostics; - } - - public static DiagnosticsUpdatedArgs DiagnosticsCreated( - object id, - Workspace workspace, - Solution? solution, - ProjectId? projectId, - DocumentId? documentId, - ImmutableArray diagnostics) - { - return new DiagnosticsUpdatedArgs(id, workspace, solution, projectId, documentId, diagnostics, DiagnosticsUpdatedKind.DiagnosticsCreated); - } - - public static DiagnosticsUpdatedArgs DiagnosticsRemoved( - object id, - Workspace workspace, - Solution? solution, - ProjectId? projectId, - DocumentId? documentId) - { - return new DiagnosticsUpdatedArgs(id, workspace, solution, projectId, documentId, [], DiagnosticsUpdatedKind.DiagnosticsRemoved); - } + return new DiagnosticsUpdatedArgs(id, workspace, solution, projectId, documentId, [], DiagnosticsUpdatedKind.DiagnosticsRemoved); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 4753f851566f1..4dc836504648a 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -9,40 +9,39 @@ using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Diagnostics +namespace Microsoft.CodeAnalysis.Diagnostics; + +[ExportIncrementalAnalyzerProvider( + name: WellKnownSolutionCrawlerAnalyzers.Diagnostic, + workspaceKinds: [WorkspaceKind.Host, WorkspaceKind.Interactive], + highPriorityForActiveFile: true)] +internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider { - [ExportIncrementalAnalyzerProvider( - name: WellKnownSolutionCrawlerAnalyzers.Diagnostic, - workspaceKinds: [WorkspaceKind.Host, WorkspaceKind.Interactive], - highPriorityForActiveFile: true)] - internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider + public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) { - public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) - { - return _map.GetValue(workspace, _createIncrementalAnalyzer); - } + return _map.GetValue(workspace, _createIncrementalAnalyzer); + } #if false - public void ShutdownAnalyzerFrom(Workspace workspace) + public void ShutdownAnalyzerFrom(Workspace workspace) + { + // this should be only called once analyzer associated with the workspace is done. + if (_map.TryGetValue(workspace, out var analyzer)) { - // this should be only called once analyzer associated with the workspace is done. - if (_map.TryGetValue(workspace, out var analyzer)) - { - analyzer.Shutdown(); - } + analyzer.Shutdown(); } + } #endif - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] - private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace) - { - // subscribe to active context changed event for new workspace - workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - - return new DiagnosticIncrementalAnalyzer(this, CorrelationIdFactory.GetNextId(), workspace, AnalyzerInfoCache); - } + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace) + { + // subscribe to active context changed event for new workspace + workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) - => Reanalyze(e.Solution.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); + return new DiagnosticIncrementalAnalyzer(this, CorrelationIdFactory.GetNextId(), workspace, AnalyzerInfoCache); } + + private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) + => Reanalyze(e.Solution.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index 47326f8cb5c55..e3256e440b77f 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -53,1549 +53,1548 @@ using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; + +/// +/// The Workspace for running inside Visual Studio. +/// +internal abstract partial class VisualStudioWorkspaceImpl : VisualStudioWorkspace { + private static readonly IntPtr s_docDataExisting_Unknown = new(-1); + private const string AppCodeFolderName = "App_Code"; + + private readonly IThreadingContext _threadingContext; + private readonly IAsyncServiceProvider _asyncServiceProvider; + private readonly ITextBufferFactoryService _textBufferFactoryService; + private readonly IProjectionBufferFactoryService _projectionBufferFactoryService; + private readonly IGlobalOptionService _globalOptions; + + private readonly ITextBufferCloneService _textBufferCloneService; + /// - /// The Workspace for running inside Visual Studio. + /// Guards any updates to the maps here that aren't updated via interlocked updates. /// - internal abstract partial class VisualStudioWorkspaceImpl : VisualStudioWorkspace - { - private static readonly IntPtr s_docDataExisting_Unknown = new(-1); - private const string AppCodeFolderName = "App_Code"; + private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1); - private readonly IThreadingContext _threadingContext; - private readonly IAsyncServiceProvider _asyncServiceProvider; - private readonly ITextBufferFactoryService _textBufferFactoryService; - private readonly IProjectionBufferFactoryService _projectionBufferFactoryService; - private readonly IGlobalOptionService _globalOptions; + /// + /// A to make assertions that stuff is on the right thread. + /// + private readonly ForegroundThreadAffinitizedObject _foregroundObject; - private readonly ITextBufferCloneService _textBufferCloneService; + private ImmutableDictionary _projectToHierarchyMap = ImmutableDictionary.Empty; + private ImmutableDictionary _projectToGuidMap = ImmutableDictionary.Empty; - /// - /// Guards any updates to the maps here that aren't updated via interlocked updates. - /// - private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1); + /// + /// A map to fetch the path to a rule set file for a project. This right now is only used to implement + /// and any other use is extremely suspicious, since direct use of this is out of + /// sync with the Workspace if there is active batching happening. + /// + /// Should be updated with . + private ImmutableDictionary> _projectToRuleSetFilePath = ImmutableDictionary>.Empty; - /// - /// A to make assertions that stuff is on the right thread. - /// - private readonly ForegroundThreadAffinitizedObject _foregroundObject; + private readonly Dictionary> _projectSystemNameToProjectsMap = []; - private ImmutableDictionary _projectToHierarchyMap = ImmutableDictionary.Empty; - private ImmutableDictionary _projectToGuidMap = ImmutableDictionary.Empty; + /// + /// Only safe to use on the UI thread. + /// + private readonly Dictionary _languageToProjectExistsUIContext = []; - /// - /// A map to fetch the path to a rule set file for a project. This right now is only used to implement - /// and any other use is extremely suspicious, since direct use of this is out of - /// sync with the Workspace if there is active batching happening. - /// - /// Should be updated with . - private ImmutableDictionary> _projectToRuleSetFilePath = ImmutableDictionary>.Empty; + private VirtualMemoryNotificationListener? _memoryListener; - private readonly Dictionary> _projectSystemNameToProjectsMap = []; + private OpenFileTracker? _openFileTracker; + internal IFileChangeWatcher FileChangeWatcher { get; } - /// - /// Only safe to use on the UI thread. - /// - private readonly Dictionary _languageToProjectExistsUIContext = []; + internal ProjectSystemProjectFactory ProjectSystemProjectFactory { get; } - private VirtualMemoryNotificationListener? _memoryListener; + private readonly Lazy _projectCodeModelFactory; + private readonly Lazy _lazyExternalErrorDiagnosticUpdateSource; + private readonly IAsynchronousOperationListener _workspaceListener; + private bool _isExternalErrorDiagnosticUpdateSourceSubscribedToSolutionBuildEvents; - private OpenFileTracker? _openFileTracker; - internal IFileChangeWatcher FileChangeWatcher { get; } + public VisualStudioWorkspaceImpl(ExportProvider exportProvider, IAsyncServiceProvider asyncServiceProvider) + : base(VisualStudioMefHostServices.Create(exportProvider)) + { + _threadingContext = exportProvider.GetExportedValue(); + _asyncServiceProvider = asyncServiceProvider; + _globalOptions = exportProvider.GetExportedValue(); + _textBufferCloneService = exportProvider.GetExportedValue(); + _textBufferFactoryService = exportProvider.GetExportedValue(); + _projectionBufferFactoryService = exportProvider.GetExportedValue(); + _projectCodeModelFactory = exportProvider.GetExport(); - internal ProjectSystemProjectFactory ProjectSystemProjectFactory { get; } + _foregroundObject = new ForegroundThreadAffinitizedObject(_threadingContext); - private readonly Lazy _projectCodeModelFactory; - private readonly Lazy _lazyExternalErrorDiagnosticUpdateSource; - private readonly IAsynchronousOperationListener _workspaceListener; - private bool _isExternalErrorDiagnosticUpdateSourceSubscribedToSolutionBuildEvents; + _textBufferFactoryService.TextBufferCreated += AddTextBufferCloneServiceToBuffer; + _projectionBufferFactoryService.ProjectionBufferCreated += AddTextBufferCloneServiceToBuffer; - public VisualStudioWorkspaceImpl(ExportProvider exportProvider, IAsyncServiceProvider asyncServiceProvider) - : base(VisualStudioMefHostServices.Create(exportProvider)) - { - _threadingContext = exportProvider.GetExportedValue(); - _asyncServiceProvider = asyncServiceProvider; - _globalOptions = exportProvider.GetExportedValue(); - _textBufferCloneService = exportProvider.GetExportedValue(); - _textBufferFactoryService = exportProvider.GetExportedValue(); - _projectionBufferFactoryService = exportProvider.GetExportedValue(); - _projectCodeModelFactory = exportProvider.GetExport(); + FileChangeWatcher = exportProvider.GetExportedValue().Watcher; - _foregroundObject = new ForegroundThreadAffinitizedObject(_threadingContext); + ProjectSystemProjectFactory = new ProjectSystemProjectFactory(this, FileChangeWatcher, CheckForAddedFileBeingOpenMaybeAsync, RemoveProjectFromMaps); - _textBufferFactoryService.TextBufferCreated += AddTextBufferCloneServiceToBuffer; - _projectionBufferFactoryService.ProjectionBufferCreated += AddTextBufferCloneServiceToBuffer; + _ = Task.Run(() => InitializeUIAffinitizedServicesAsync(asyncServiceProvider)); - FileChangeWatcher = exportProvider.GetExportedValue().Watcher; + _lazyExternalErrorDiagnosticUpdateSource = new Lazy(() => + new ExternalErrorDiagnosticUpdateSource( + this, + exportProvider.GetExportedValue(), + exportProvider.GetExportedValue(), + exportProvider.GetExportedValue(), + exportProvider.GetExportedValue(), + _threadingContext), + isThreadSafe: true); - ProjectSystemProjectFactory = new ProjectSystemProjectFactory(this, FileChangeWatcher, CheckForAddedFileBeingOpenMaybeAsync, RemoveProjectFromMaps); + _workspaceListener = Services.GetRequiredService().GetListener(); + } - _ = Task.Run(() => InitializeUIAffinitizedServicesAsync(asyncServiceProvider)); + internal ExternalErrorDiagnosticUpdateSource ExternalErrorDiagnosticUpdateSource => _lazyExternalErrorDiagnosticUpdateSource.Value; - _lazyExternalErrorDiagnosticUpdateSource = new Lazy(() => - new ExternalErrorDiagnosticUpdateSource( - this, - exportProvider.GetExportedValue(), - exportProvider.GetExportedValue(), - exportProvider.GetExportedValue(), - exportProvider.GetExportedValue(), - _threadingContext), - isThreadSafe: true); + internal void SubscribeExternalErrorDiagnosticUpdateSourceToSolutionBuildEvents() + { + // TODO: further understand if this needs the foreground thread for any reason. UIContexts are safe to read from the UI thread; + // it's not clear to me why this is being asserted. + _foregroundObject.AssertIsForeground(); - _workspaceListener = Services.GetRequiredService().GetListener(); + if (_isExternalErrorDiagnosticUpdateSourceSubscribedToSolutionBuildEvents) + { + return; } - internal ExternalErrorDiagnosticUpdateSource ExternalErrorDiagnosticUpdateSource => _lazyExternalErrorDiagnosticUpdateSource.Value; - - internal void SubscribeExternalErrorDiagnosticUpdateSourceToSolutionBuildEvents() + // TODO: https://github.com/dotnet/roslyn/issues/36065 + // UIContextImpl requires IVsMonitorSelection service: + if (ServiceProvider.GlobalProvider.GetService(typeof(IVsMonitorSelection)) == null) { - // TODO: further understand if this needs the foreground thread for any reason. UIContexts are safe to read from the UI thread; - // it's not clear to me why this is being asserted. - _foregroundObject.AssertIsForeground(); - - if (_isExternalErrorDiagnosticUpdateSourceSubscribedToSolutionBuildEvents) - { - return; - } - - // TODO: https://github.com/dotnet/roslyn/issues/36065 - // UIContextImpl requires IVsMonitorSelection service: - if (ServiceProvider.GlobalProvider.GetService(typeof(IVsMonitorSelection)) == null) - { - return; - } + return; + } - // This pattern ensures that we are called whenever the build starts/completes even if it is already in progress. - KnownUIContexts.SolutionBuildingContext.WhenActivated(() => + // This pattern ensures that we are called whenever the build starts/completes even if it is already in progress. + KnownUIContexts.SolutionBuildingContext.WhenActivated(() => + { + KnownUIContexts.SolutionBuildingContext.UIContextChanged += (object _, UIContextChangedEventArgs e) => { - KnownUIContexts.SolutionBuildingContext.UIContextChanged += (object _, UIContextChangedEventArgs e) => + if (e.Activated) { - if (e.Activated) - { - ExternalErrorDiagnosticUpdateSource.OnSolutionBuildStarted(); - } - else - { - // A real build just finished. Clear out any results from the last "run code analysis" command. - this.Services.GetRequiredService().Clear(); - ExternalErrorDiagnosticUpdateSource.OnSolutionBuildCompleted(); - } - }; + ExternalErrorDiagnosticUpdateSource.OnSolutionBuildStarted(); + } + else + { + // A real build just finished. Clear out any results from the last "run code analysis" command. + this.Services.GetRequiredService().Clear(); + ExternalErrorDiagnosticUpdateSource.OnSolutionBuildCompleted(); + } + }; - ExternalErrorDiagnosticUpdateSource.OnSolutionBuildStarted(); - }); + ExternalErrorDiagnosticUpdateSource.OnSolutionBuildStarted(); + }); - _isExternalErrorDiagnosticUpdateSourceSubscribedToSolutionBuildEvents = true; - } + _isExternalErrorDiagnosticUpdateSourceSubscribedToSolutionBuildEvents = true; + } - public async Task InitializeUIAffinitizedServicesAsync(IAsyncServiceProvider asyncServiceProvider) - { - // Create services that are bound to the UI thread - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); + public async Task InitializeUIAffinitizedServicesAsync(IAsyncServiceProvider asyncServiceProvider) + { + // Create services that are bound to the UI thread + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); - // Fetch the session synchronously on the UI thread; if this doesn't happen before we try using this on - // the background thread then we will experience hangs like we see in this bug: - // https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?_a=edit&id=190808 or - // https://devdiv.visualstudio.com/DevDiv/_workitems?id=296981&_a=edit - var telemetrySession = TelemetryService.DefaultSession; + // Fetch the session synchronously on the UI thread; if this doesn't happen before we try using this on + // the background thread then we will experience hangs like we see in this bug: + // https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?_a=edit&id=190808 or + // https://devdiv.visualstudio.com/DevDiv/_workitems?id=296981&_a=edit + var telemetrySession = TelemetryService.DefaultSession; - var solutionClosingContext = UIContext.FromUIContextGuid(VSConstants.UICONTEXT.SolutionClosing_guid); - solutionClosingContext.UIContextChanged += (_, e) => ProjectSystemProjectFactory.SolutionClosing = e.Activated; + var solutionClosingContext = UIContext.FromUIContextGuid(VSConstants.UICONTEXT.SolutionClosing_guid); + solutionClosingContext.UIContextChanged += (_, e) => ProjectSystemProjectFactory.SolutionClosing = e.Activated; - var openFileTracker = await OpenFileTracker.CreateAsync(this, ProjectSystemProjectFactory, asyncServiceProvider).ConfigureAwait(true); - var memoryListener = await VirtualMemoryNotificationListener.CreateAsync(this, _threadingContext, asyncServiceProvider, _globalOptions, _threadingContext.DisposalToken).ConfigureAwait(true); + var openFileTracker = await OpenFileTracker.CreateAsync(this, ProjectSystemProjectFactory, asyncServiceProvider).ConfigureAwait(true); + var memoryListener = await VirtualMemoryNotificationListener.CreateAsync(this, _threadingContext, asyncServiceProvider, _globalOptions, _threadingContext.DisposalToken).ConfigureAwait(true); - // Update our fields first, so any asynchronous work that needs to use these is able to see the service. - // WARNING: if we do .ConfigureAwait(true) here, it means we're trying to transition to the UI thread while - // semaphore is acquired; if the UI thread is blocked trying to acquire the semaphore, we could deadlock. - using (await _gate.DisposableWaitAsync().ConfigureAwait(false)) - { - _openFileTracker = openFileTracker; - _memoryListener = memoryListener; - } + // Update our fields first, so any asynchronous work that needs to use these is able to see the service. + // WARNING: if we do .ConfigureAwait(true) here, it means we're trying to transition to the UI thread while + // semaphore is acquired; if the UI thread is blocked trying to acquire the semaphore, we could deadlock. + using (await _gate.DisposableWaitAsync().ConfigureAwait(false)) + { + _openFileTracker = openFileTracker; + _memoryListener = memoryListener; + } - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); - // This must be called after the _openFileTracker was assigned; this way we know that a file added from the project system either got checked - // in CheckForAddedFileBeingOpenMaybeAsync, or we catch it here. - openFileTracker.CheckForOpenFilesThatWeMissed(); + // This must be called after the _openFileTracker was assigned; this way we know that a file added from the project system either got checked + // in CheckForAddedFileBeingOpenMaybeAsync, or we catch it here. + openFileTracker.CheckForOpenFilesThatWeMissed(); - // Switch to a background thread to avoid loading option providers on UI thread (telemetry is reading options). - await TaskScheduler.Default; + // Switch to a background thread to avoid loading option providers on UI thread (telemetry is reading options). + await TaskScheduler.Default; - var logDelta = _globalOptions.GetOption(DiagnosticOptionsStorage.LogTelemetryForBackgroundAnalyzerExecution); - var telemetryService = (VisualStudioWorkspaceTelemetryService)Services.GetRequiredService(); - telemetryService.InitializeTelemetrySession(telemetrySession, logDelta); + var logDelta = _globalOptions.GetOption(DiagnosticOptionsStorage.LogTelemetryForBackgroundAnalyzerExecution); + var telemetryService = (VisualStudioWorkspaceTelemetryService)Services.GetRequiredService(); + telemetryService.InitializeTelemetrySession(telemetrySession, logDelta); - Logger.Log(FunctionId.Run_Environment, - KeyValueLogMessage.Create(m => m["Version"] = FileVersionInfo.GetVersionInfo(typeof(VisualStudioWorkspace).Assembly.Location).FileVersion)); - } + Logger.Log(FunctionId.Run_Environment, + KeyValueLogMessage.Create(m => m["Version"] = FileVersionInfo.GetVersionInfo(typeof(VisualStudioWorkspace).Assembly.Location).FileVersion)); + } - public Task CheckForAddedFileBeingOpenMaybeAsync(bool useAsync, ImmutableArray newFileNames) - => _openFileTracker?.CheckForAddedFileBeingOpenMaybeAsync(useAsync, newFileNames) ?? Task.CompletedTask; + public Task CheckForAddedFileBeingOpenMaybeAsync(bool useAsync, ImmutableArray newFileNames) + => _openFileTracker?.CheckForAddedFileBeingOpenMaybeAsync(useAsync, newFileNames) ?? Task.CompletedTask; - internal void AddProjectToInternalMaps(ProjectSystemProject project, IVsHierarchy? hierarchy, Guid guid, string projectSystemName) + internal void AddProjectToInternalMaps(ProjectSystemProject project, IVsHierarchy? hierarchy, Guid guid, string projectSystemName) + { + using (_gate.DisposableWait()) { - using (_gate.DisposableWait()) - { - _projectToHierarchyMap = _projectToHierarchyMap.Add(project.Id, hierarchy); - _projectToGuidMap = _projectToGuidMap.Add(project.Id, guid); - _projectSystemNameToProjectsMap.MultiAdd(projectSystemName, project); - } + _projectToHierarchyMap = _projectToHierarchyMap.Add(project.Id, hierarchy); + _projectToGuidMap = _projectToGuidMap.Add(project.Id, guid); + _projectSystemNameToProjectsMap.MultiAdd(projectSystemName, project); } + } - internal void AddProjectRuleSetFileToInternalMaps(ProjectSystemProject project, Func ruleSetFilePathFunc) - { - Contract.ThrowIfFalse(ImmutableInterlocked.TryAdd(ref _projectToRuleSetFilePath, project.Id, ruleSetFilePathFunc)); - } + internal void AddProjectRuleSetFileToInternalMaps(ProjectSystemProject project, Func ruleSetFilePathFunc) + { + Contract.ThrowIfFalse(ImmutableInterlocked.TryAdd(ref _projectToRuleSetFilePath, project.Id, ruleSetFilePathFunc)); + } - internal ProjectSystemProject? GetProjectWithHierarchyAndName(IVsHierarchy hierarchy, string projectName) + internal ProjectSystemProject? GetProjectWithHierarchyAndName(IVsHierarchy hierarchy, string projectName) + { + using (_gate.DisposableWait()) { - using (_gate.DisposableWait()) - { - return GetProjectWithHierarchyAndName_NoLock(hierarchy, projectName); - } + return GetProjectWithHierarchyAndName_NoLock(hierarchy, projectName); } + } - private ProjectSystemProject? GetProjectWithHierarchyAndName_NoLock(IVsHierarchy hierarchy, string projectName) + private ProjectSystemProject? GetProjectWithHierarchyAndName_NoLock(IVsHierarchy hierarchy, string projectName) + { + if (_projectSystemNameToProjectsMap.TryGetValue(projectName, out var projects)) { - if (_projectSystemNameToProjectsMap.TryGetValue(projectName, out var projects)) + foreach (var project in projects) { - foreach (var project in projects) + if (_projectToHierarchyMap.TryGetValue(project.Id, out var projectHierarchy)) { - if (_projectToHierarchyMap.TryGetValue(project.Id, out var projectHierarchy)) + if (projectHierarchy == hierarchy) { - if (projectHierarchy == hierarchy) - { - return project; - } + return project; } } } + } + + return null; + } + // TODO: consider whether this should be going to the project system directly to get this path. This is only called on interactions from the + // Solution Explorer in the SolutionExplorerShim, where if we just could more directly get to the rule set file it'd simplify this. + internal override string? TryGetRuleSetPathForProject(ProjectId projectId) + { + // _projectToRuleSetFilePath is immutable, so can be used outside of locks + if (_projectToRuleSetFilePath.TryGetValue(projectId, out var ruleSetPathFunc)) + { + return ruleSetPathFunc(); + } + else + { return null; } + } - // TODO: consider whether this should be going to the project system directly to get this path. This is only called on interactions from the - // Solution Explorer in the SolutionExplorerShim, where if we just could more directly get to the rule set file it'd simplify this. - internal override string? TryGetRuleSetPathForProject(ProjectId projectId) + public override EnvDTE.FileCodeModel GetFileCodeModel(DocumentId documentId) + { + if (documentId == null) { - // _projectToRuleSetFilePath is immutable, so can be used outside of locks - if (_projectToRuleSetFilePath.TryGetValue(projectId, out var ruleSetPathFunc)) - { - return ruleSetPathFunc(); - } - else - { - return null; - } + throw new ArgumentNullException(nameof(documentId)); } - public override EnvDTE.FileCodeModel GetFileCodeModel(DocumentId documentId) + var document = _threadingContext.JoinableTaskFactory.Run(() => CurrentSolution.GetDocumentAsync(documentId, includeSourceGenerated: true).AsTask()); + if (document == null) { - if (documentId == null) - { - throw new ArgumentNullException(nameof(documentId)); - } - - var document = _threadingContext.JoinableTaskFactory.Run(() => CurrentSolution.GetDocumentAsync(documentId, includeSourceGenerated: true).AsTask()); - if (document == null) - { - throw new ArgumentException(ServicesVSResources.The_given_DocumentId_did_not_come_from_the_Visual_Studio_workspace, nameof(documentId)); - } - - if (document is SourceGeneratedDocument sourceGeneratedDocument) - { - return _projectCodeModelFactory.Value.CreateFileCodeModel(sourceGeneratedDocument); - } - else - { - return _projectCodeModelFactory.Value.GetOrCreateFileCodeModel(documentId.ProjectId, document.FilePath); - } + throw new ArgumentException(ServicesVSResources.The_given_DocumentId_did_not_come_from_the_Visual_Studio_workspace, nameof(documentId)); } - internal override bool TryApplyChanges( - Microsoft.CodeAnalysis.Solution newSolution, - IProgress progressTracker) + if (document is SourceGeneratedDocument sourceGeneratedDocument) { - if (!_foregroundObject.IsForeground()) - { - throw new InvalidOperationException(ServicesVSResources.VisualStudioWorkspace_TryApplyChanges_cannot_be_called_from_a_background_thread); - } - - var currentSolution = this.CurrentSolution; - var projectChanges = newSolution.GetChanges(currentSolution).GetProjectChanges().ToList(); + return _projectCodeModelFactory.Value.CreateFileCodeModel(sourceGeneratedDocument); + } + else + { + return _projectCodeModelFactory.Value.GetOrCreateFileCodeModel(documentId.ProjectId, document.FilePath); + } + } - // first make sure we can edit the document we will be updating (check them out from source control, etc) - var changedDocs = projectChanges.SelectMany(pd => pd.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true).Concat(pd.GetChangedAdditionalDocuments())) - .Where(CanApplyChange).ToList(); - if (changedDocs.Count > 0) - { - this.EnsureEditableDocuments(changedDocs); - } + internal override bool TryApplyChanges( + Microsoft.CodeAnalysis.Solution newSolution, + IProgress progressTracker) + { + if (!_foregroundObject.IsForeground()) + { + throw new InvalidOperationException(ServicesVSResources.VisualStudioWorkspace_TryApplyChanges_cannot_be_called_from_a_background_thread); + } - return base.TryApplyChanges(newSolution, progressTracker); + var currentSolution = this.CurrentSolution; + var projectChanges = newSolution.GetChanges(currentSolution).GetProjectChanges().ToList(); - bool CanApplyChange(DocumentId documentId) - { - var document = newSolution.GetDocument(documentId) ?? currentSolution.GetDocument(documentId); - if (document == null) - { - // we can have null if documentId is for additional files - return true; - } - - return document.CanApplyChange(); - } + // first make sure we can edit the document we will be updating (check them out from source control, etc) + var changedDocs = projectChanges.SelectMany(pd => pd.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true).Concat(pd.GetChangedAdditionalDocuments())) + .Where(CanApplyChange).ToList(); + if (changedDocs.Count > 0) + { + this.EnsureEditableDocuments(changedDocs); } - public override bool CanOpenDocuments + return base.TryApplyChanges(newSolution, progressTracker); + + bool CanApplyChange(DocumentId documentId) { - get + var document = newSolution.GetDocument(documentId) ?? currentSolution.GetDocument(documentId); + if (document == null) { + // we can have null if documentId is for additional files return true; } + + return document.CanApplyChange(); } + } - internal override bool CanChangeActiveContextDocument + public override bool CanOpenDocuments + { + get { - get - { - return true; - } + return true; } + } - internal bool IsCPSProject(CodeAnalysis.Project project) - => IsCPSProject(project.Id); - - internal bool IsCPSProject(ProjectId projectId) + internal override bool CanChangeActiveContextDocument + { + get { - _foregroundObject.AssertIsForeground(); + return true; + } + } - if (this.TryGetHierarchy(projectId, out var hierarchy)) - { - return hierarchy.IsCapabilityMatch("CPS"); - } + internal bool IsCPSProject(CodeAnalysis.Project project) + => IsCPSProject(project.Id); - return false; + internal bool IsCPSProject(ProjectId projectId) + { + _foregroundObject.AssertIsForeground(); + + if (this.TryGetHierarchy(projectId, out var hierarchy)) + { + return hierarchy.IsCapabilityMatch("CPS"); } - internal bool IsPrimaryProject(ProjectId projectId) + return false; + } + + internal bool IsPrimaryProject(ProjectId projectId) + { + using (_gate.DisposableWait()) { - using (_gate.DisposableWait()) + foreach (var (_, projects) in _projectSystemNameToProjectsMap) { - foreach (var (_, projects) in _projectSystemNameToProjectsMap) + foreach (var project in projects) { - foreach (var project in projects) - { - if (project.Id == projectId) - return project.IsPrimary; - } + if (project.Id == projectId) + return project.IsPrimary; } } - - return true; } - public override bool CanApplyCompilationOptionChange(CompilationOptions oldOptions, CompilationOptions newOptions, CodeAnalysis.Project project) - => project.Services.GetRequiredService().CanApplyChange(oldOptions, newOptions); + return true; + } - public override bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, CodeAnalysis.Project project) - { - var maxSupportLangVersion = ProjectSystemProjectFactory.TryGetMaxSupportedLanguageVersion(project.Id); + public override bool CanApplyCompilationOptionChange(CompilationOptions oldOptions, CompilationOptions newOptions, CodeAnalysis.Project project) + => project.Services.GetRequiredService().CanApplyChange(oldOptions, newOptions); - return project.Services.GetRequiredService().CanApplyChange(oldOptions, newOptions, maxSupportLangVersion); - } + public override bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, CodeAnalysis.Project project) + { + var maxSupportLangVersion = ProjectSystemProjectFactory.TryGetMaxSupportedLanguageVersion(project.Id); + + return project.Services.GetRequiredService().CanApplyChange(oldOptions, newOptions, maxSupportLangVersion); + } - private void AddTextBufferCloneServiceToBuffer(object sender, TextBufferCreatedEventArgs e) - => e.TextBuffer.Properties.AddProperty(typeof(ITextBufferCloneService), _textBufferCloneService); + private void AddTextBufferCloneServiceToBuffer(object sender, TextBufferCreatedEventArgs e) + => e.TextBuffer.Properties.AddProperty(typeof(ITextBufferCloneService), _textBufferCloneService); - public override bool CanApplyChange(ApplyChangesKind feature) + public override bool CanApplyChange(ApplyChangesKind feature) + { + switch (feature) { - switch (feature) - { - case ApplyChangesKind.AddDocument: - case ApplyChangesKind.RemoveDocument: - case ApplyChangesKind.ChangeDocument: - case ApplyChangesKind.AddMetadataReference: - case ApplyChangesKind.RemoveMetadataReference: - case ApplyChangesKind.AddProjectReference: - case ApplyChangesKind.RemoveProjectReference: - case ApplyChangesKind.AddAnalyzerReference: - case ApplyChangesKind.RemoveAnalyzerReference: - case ApplyChangesKind.AddAdditionalDocument: - case ApplyChangesKind.RemoveAdditionalDocument: - case ApplyChangesKind.ChangeAdditionalDocument: - case ApplyChangesKind.ChangeCompilationOptions: - case ApplyChangesKind.ChangeParseOptions: - case ApplyChangesKind.ChangeDocumentInfo: - case ApplyChangesKind.AddAnalyzerConfigDocument: - case ApplyChangesKind.RemoveAnalyzerConfigDocument: - case ApplyChangesKind.ChangeAnalyzerConfigDocument: - case ApplyChangesKind.AddSolutionAnalyzerReference: - case ApplyChangesKind.RemoveSolutionAnalyzerReference: - return true; + case ApplyChangesKind.AddDocument: + case ApplyChangesKind.RemoveDocument: + case ApplyChangesKind.ChangeDocument: + case ApplyChangesKind.AddMetadataReference: + case ApplyChangesKind.RemoveMetadataReference: + case ApplyChangesKind.AddProjectReference: + case ApplyChangesKind.RemoveProjectReference: + case ApplyChangesKind.AddAnalyzerReference: + case ApplyChangesKind.RemoveAnalyzerReference: + case ApplyChangesKind.AddAdditionalDocument: + case ApplyChangesKind.RemoveAdditionalDocument: + case ApplyChangesKind.ChangeAdditionalDocument: + case ApplyChangesKind.ChangeCompilationOptions: + case ApplyChangesKind.ChangeParseOptions: + case ApplyChangesKind.ChangeDocumentInfo: + case ApplyChangesKind.AddAnalyzerConfigDocument: + case ApplyChangesKind.RemoveAnalyzerConfigDocument: + case ApplyChangesKind.ChangeAnalyzerConfigDocument: + case ApplyChangesKind.AddSolutionAnalyzerReference: + case ApplyChangesKind.RemoveSolutionAnalyzerReference: + return true; - default: - return false; - } + default: + return false; } + } - private bool TryGetProjectData(ProjectId projectId, [NotNullWhen(returnValue: true)] out IVsHierarchy? hierarchy, [NotNullWhen(returnValue: true)] out EnvDTE.Project? project) - { - project = null; + private bool TryGetProjectData(ProjectId projectId, [NotNullWhen(returnValue: true)] out IVsHierarchy? hierarchy, [NotNullWhen(returnValue: true)] out EnvDTE.Project? project) + { + project = null; - return - this.TryGetHierarchy(projectId, out hierarchy) && - hierarchy.TryGetProject(out project); - } + return + this.TryGetHierarchy(projectId, out hierarchy) && + hierarchy.TryGetProject(out project); + } - internal void GetProjectData(ProjectId projectId, out IVsHierarchy hierarchy, out EnvDTE.Project project) + internal void GetProjectData(ProjectId projectId, out IVsHierarchy hierarchy, out EnvDTE.Project project) + { + if (!TryGetProjectData(projectId, out hierarchy!, out project!)) { - if (!TryGetProjectData(projectId, out hierarchy!, out project!)) - { - throw new ArgumentException(string.Format(ServicesVSResources.Could_not_find_project_0, projectId)); - } + throw new ArgumentException(string.Format(ServicesVSResources.Could_not_find_project_0, projectId)); } + } - internal EnvDTE.Project? TryGetDTEProject(ProjectId projectId) - => TryGetProjectData(projectId, out var _, out var project) ? project : null; + internal EnvDTE.Project? TryGetDTEProject(ProjectId projectId) + => TryGetProjectData(projectId, out var _, out var project) ? project : null; - internal bool TryAddReferenceToProject(ProjectId projectId, string assemblyName) + internal bool TryAddReferenceToProject(ProjectId projectId, string assemblyName) + { + if (!TryGetProjectData(projectId, out _, out var project)) { - if (!TryGetProjectData(projectId, out _, out var project)) - { - return false; - } - - var vsProject = (VSProject)project.Object; - try - { - vsProject.References.Add(assemblyName); - } - catch (Exception) - { - return false; - } + return false; + } - return true; + var vsProject = (VSProject)project.Object; + try + { + vsProject.References.Add(assemblyName); } + catch (Exception) + { + return false; + } + + return true; + } - private static string? GetAnalyzerPath(AnalyzerReference analyzerReference) - => analyzerReference.FullPath; + private static string? GetAnalyzerPath(AnalyzerReference analyzerReference) + => analyzerReference.FullPath; - protected override void ApplyCompilationOptionsChanged(ProjectId projectId, CompilationOptions options) + protected override void ApplyCompilationOptionsChanged(ProjectId projectId, CompilationOptions options) + { + if (projectId == null) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } + throw new ArgumentNullException(nameof(projectId)); + } - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } - var originalProject = CurrentSolution.GetRequiredProject(projectId); - var compilationOptionsService = originalProject.Services.GetRequiredService(); - var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId), ServiceProvider.GlobalProvider); - compilationOptionsService.Apply(originalProject.CompilationOptions!, options, storage); + var originalProject = CurrentSolution.GetRequiredProject(projectId); + var compilationOptionsService = originalProject.Services.GetRequiredService(); + var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId), ServiceProvider.GlobalProvider); + compilationOptionsService.Apply(originalProject.CompilationOptions!, options, storage); + } + + protected override void ApplyParseOptionsChanged(ProjectId projectId, ParseOptions options) + { + if (projectId == null) + { + throw new ArgumentNullException(nameof(projectId)); } - protected override void ApplyParseOptionsChanged(ProjectId projectId, ParseOptions options) + if (options == null) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } + throw new ArgumentNullException(nameof(options)); + } - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } + var parseOptionsService = CurrentSolution.GetRequiredProject(projectId).Services.GetRequiredService(); + var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId), ServiceProvider.GlobalProvider); + parseOptionsService.Apply(options, storage); + } - var parseOptionsService = CurrentSolution.GetRequiredProject(projectId).Services.GetRequiredService(); - var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId), ServiceProvider.GlobalProvider); - parseOptionsService.Apply(options, storage); + protected override void ApplyAnalyzerReferenceAdded(ProjectId projectId, AnalyzerReference analyzerReference) + { + if (projectId == null) + { + throw new ArgumentNullException(nameof(projectId)); } - protected override void ApplyAnalyzerReferenceAdded(ProjectId projectId, AnalyzerReference analyzerReference) + if (analyzerReference == null) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } - - if (analyzerReference == null) - { - throw new ArgumentNullException(nameof(analyzerReference)); - } + throw new ArgumentNullException(nameof(analyzerReference)); + } - GetProjectData(projectId, out _, out var project); + GetProjectData(projectId, out _, out var project); - var filePath = GetAnalyzerPath(analyzerReference); - if (filePath != null) - { - var vsProject = (VSProject3)project.Object; - vsProject.AnalyzerReferences.Add(filePath); - } + var filePath = GetAnalyzerPath(analyzerReference); + if (filePath != null) + { + var vsProject = (VSProject3)project.Object; + vsProject.AnalyzerReferences.Add(filePath); } + } - protected override void ApplyAnalyzerReferenceRemoved(ProjectId projectId, AnalyzerReference analyzerReference) + protected override void ApplyAnalyzerReferenceRemoved(ProjectId projectId, AnalyzerReference analyzerReference) + { + if (projectId == null) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } + throw new ArgumentNullException(nameof(projectId)); + } - if (analyzerReference == null) - { - throw new ArgumentNullException(nameof(analyzerReference)); - } + if (analyzerReference == null) + { + throw new ArgumentNullException(nameof(analyzerReference)); + } - GetProjectData(projectId, out _, out var project); + GetProjectData(projectId, out _, out var project); - var filePath = GetAnalyzerPath(analyzerReference); - if (filePath != null) - { - var vsProject = (VSProject3)project.Object; - vsProject.AnalyzerReferences.Remove(filePath); - } + var filePath = GetAnalyzerPath(analyzerReference); + if (filePath != null) + { + var vsProject = (VSProject3)project.Object; + vsProject.AnalyzerReferences.Remove(filePath); } + } - private static string? GetMetadataPath(MetadataReference metadataReference) + private static string? GetMetadataPath(MetadataReference metadataReference) + { + if (metadataReference is PortableExecutableReference fileMetadata) { - if (metadataReference is PortableExecutableReference fileMetadata) - { - return fileMetadata.FilePath; - } - - return null; + return fileMetadata.FilePath; } - protected override void ApplyMetadataReferenceAdded( - ProjectId projectId, MetadataReference metadataReference) + return null; + } + + protected override void ApplyMetadataReferenceAdded( + ProjectId projectId, MetadataReference metadataReference) + { + if (projectId == null) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } + throw new ArgumentNullException(nameof(projectId)); + } - if (metadataReference == null) - { - throw new ArgumentNullException(nameof(metadataReference)); - } + if (metadataReference == null) + { + throw new ArgumentNullException(nameof(metadataReference)); + } - GetProjectData(projectId, out _, out var project); + GetProjectData(projectId, out _, out var project); - var filePath = GetMetadataPath(metadataReference); - if (filePath != null) - { - var vsProject = (VSProject)project.Object; - vsProject.References.Add(filePath); + var filePath = GetMetadataPath(metadataReference); + if (filePath != null) + { + var vsProject = (VSProject)project.Object; + vsProject.References.Add(filePath); - var undoManager = TryGetUndoManager(); - undoManager?.Add(new RemoveMetadataReferenceUndoUnit(this, projectId, filePath)); - } + var undoManager = TryGetUndoManager(); + undoManager?.Add(new RemoveMetadataReferenceUndoUnit(this, projectId, filePath)); } + } - protected override void ApplyMetadataReferenceRemoved( - ProjectId projectId, MetadataReference metadataReference) + protected override void ApplyMetadataReferenceRemoved( + ProjectId projectId, MetadataReference metadataReference) + { + if (projectId == null) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } + throw new ArgumentNullException(nameof(projectId)); + } - if (metadataReference == null) - { - throw new ArgumentNullException(nameof(metadataReference)); - } + if (metadataReference == null) + { + throw new ArgumentNullException(nameof(metadataReference)); + } - GetProjectData(projectId, out _, out var project); + GetProjectData(projectId, out _, out var project); - var filePath = GetMetadataPath(metadataReference); - if (filePath != null) + var filePath = GetMetadataPath(metadataReference); + if (filePath != null) + { + var vsProject = (VSProject)project.Object; + foreach (Reference reference in vsProject.References) { - var vsProject = (VSProject)project.Object; - foreach (Reference reference in vsProject.References) + if (StringComparer.OrdinalIgnoreCase.Equals(reference.Path, filePath)) { - if (StringComparer.OrdinalIgnoreCase.Equals(reference.Path, filePath)) - { - reference.Remove(); - var undoManager = TryGetUndoManager(); - undoManager?.Add(new AddMetadataReferenceUndoUnit(this, projectId, filePath)); - break; - } + reference.Remove(); + var undoManager = TryGetUndoManager(); + undoManager?.Add(new AddMetadataReferenceUndoUnit(this, projectId, filePath)); + break; } } } + } - internal override void ApplyMappedFileChanges(SolutionChanges solutionChanges) + internal override void ApplyMappedFileChanges(SolutionChanges solutionChanges) + { + // Get the original text changes from all documents and call the span mapping service to get span mappings for the text changes. + // Create mapped text changes using the mapped spans and original text changes' text. + + // Mappings for opened razor files are retrieved via the LSP client making a request to the razor server. + // If we wait for the result on the UI thread, we will hit a bug in the LSP client that brings us to a code path + // using ConfigureAwait(true). This deadlocks as it then attempts to return to the UI thread which is already blocked by us. + // Instead, we invoke this in JTF run which will mitigate deadlocks when the ConfigureAwait(true) + // tries to switch back to the main thread in the LSP client. + // Link to LSP client bug for ConfigureAwait(true) - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1216657 + var mappedChanges = _threadingContext.JoinableTaskFactory.Run(() => GetMappedTextChangesAsync(solutionChanges)); + + // Group the mapped text changes by file, then apply all mapped text changes for the file. + foreach (var changesForFile in mappedChanges) { - // Get the original text changes from all documents and call the span mapping service to get span mappings for the text changes. - // Create mapped text changes using the mapped spans and original text changes' text. - - // Mappings for opened razor files are retrieved via the LSP client making a request to the razor server. - // If we wait for the result on the UI thread, we will hit a bug in the LSP client that brings us to a code path - // using ConfigureAwait(true). This deadlocks as it then attempts to return to the UI thread which is already blocked by us. - // Instead, we invoke this in JTF run which will mitigate deadlocks when the ConfigureAwait(true) - // tries to switch back to the main thread in the LSP client. - // Link to LSP client bug for ConfigureAwait(true) - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1216657 - var mappedChanges = _threadingContext.JoinableTaskFactory.Run(() => GetMappedTextChangesAsync(solutionChanges)); - - // Group the mapped text changes by file, then apply all mapped text changes for the file. - foreach (var changesForFile in mappedChanges) - { - // It doesn't matter which of the file's projectIds we pass to the invisible editor, so just pick the first. - var projectId = changesForFile.Value.First().ProjectId; - // Make sure we only take distinct changes - we'll have duplicates from different projects for linked files or multi-targeted files. - var distinctTextChanges = changesForFile.Value.Select(change => change.TextChange).Distinct().ToImmutableArray(); - using var invisibleEditor = new InvisibleEditor(ServiceProvider.GlobalProvider, changesForFile.Key, GetHierarchy(projectId), needsSave: true, needsUndoDisabled: false); - TextEditApplication.UpdateText(distinctTextChanges, invisibleEditor.TextBuffer, EditOptions.None); - } + // It doesn't matter which of the file's projectIds we pass to the invisible editor, so just pick the first. + var projectId = changesForFile.Value.First().ProjectId; + // Make sure we only take distinct changes - we'll have duplicates from different projects for linked files or multi-targeted files. + var distinctTextChanges = changesForFile.Value.Select(change => change.TextChange).Distinct().ToImmutableArray(); + using var invisibleEditor = new InvisibleEditor(ServiceProvider.GlobalProvider, changesForFile.Key, GetHierarchy(projectId), needsSave: true, needsUndoDisabled: false); + TextEditApplication.UpdateText(distinctTextChanges, invisibleEditor.TextBuffer, EditOptions.None); + } - return; + return; - async Task> GetMappedTextChangesAsync(SolutionChanges solutionChanges) + async Task> GetMappedTextChangesAsync(SolutionChanges solutionChanges) + { + var filePathToMappedTextChanges = new MultiDictionary(); + foreach (var projectChanges in solutionChanges.GetProjectChanges()) { - var filePathToMappedTextChanges = new MultiDictionary(); - foreach (var projectChanges in solutionChanges.GetProjectChanges()) + foreach (var changedDocumentId in projectChanges.GetChangedDocuments()) { - foreach (var changedDocumentId in projectChanges.GetChangedDocuments()) + var oldDocument = projectChanges.OldProject.GetRequiredDocument(changedDocumentId); + if (!ShouldApplyChangesToMappedDocuments(oldDocument, out var mappingService)) { - var oldDocument = projectChanges.OldProject.GetRequiredDocument(changedDocumentId); - if (!ShouldApplyChangesToMappedDocuments(oldDocument, out var mappingService)) - { - continue; - } - - var newDocument = projectChanges.NewProject.GetRequiredDocument(changedDocumentId); - var mappedTextChanges = await mappingService.GetMappedTextChangesAsync( - oldDocument, newDocument, CancellationToken.None).ConfigureAwait(false); - foreach (var (filePath, textChange) in mappedTextChanges) - { - filePathToMappedTextChanges.Add(filePath, (textChange, projectChanges.ProjectId)); - } + continue; } - } - return filePathToMappedTextChanges; + var newDocument = projectChanges.NewProject.GetRequiredDocument(changedDocumentId); + var mappedTextChanges = await mappingService.GetMappedTextChangesAsync( + oldDocument, newDocument, CancellationToken.None).ConfigureAwait(false); + foreach (var (filePath, textChange) in mappedTextChanges) + { + filePathToMappedTextChanges.Add(filePath, (textChange, projectChanges.ProjectId)); + } + } } - bool ShouldApplyChangesToMappedDocuments(CodeAnalysis.Document document, [NotNullWhen(true)] out ISpanMappingService? spanMappingService) - { - spanMappingService = document.Services.GetService(); - // Only consider files that are mapped and that we are unable to apply changes to. - // TODO - refactor how this is determined - https://github.com/dotnet/roslyn/issues/47908 - return spanMappingService != null && document?.CanApplyChange() == false; - } + return filePathToMappedTextChanges; } - protected override void ApplyProjectReferenceAdded( - ProjectId projectId, ProjectReference projectReference) + bool ShouldApplyChangesToMappedDocuments(CodeAnalysis.Document document, [NotNullWhen(true)] out ISpanMappingService? spanMappingService) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } + spanMappingService = document.Services.GetService(); + // Only consider files that are mapped and that we are unable to apply changes to. + // TODO - refactor how this is determined - https://github.com/dotnet/roslyn/issues/47908 + return spanMappingService != null && document?.CanApplyChange() == false; + } + } - if (projectReference == null) - { - throw new ArgumentNullException(nameof(projectReference)); - } + protected override void ApplyProjectReferenceAdded( + ProjectId projectId, ProjectReference projectReference) + { + if (projectId == null) + { + throw new ArgumentNullException(nameof(projectId)); + } - GetProjectData(projectId, out _, out var project); - GetProjectData(projectReference.ProjectId, out _, out var refProject); + if (projectReference == null) + { + throw new ArgumentNullException(nameof(projectReference)); + } - var vsProject = (VSProject)project.Object; - vsProject.References.AddProject(refProject); + GetProjectData(projectId, out _, out var project); + GetProjectData(projectReference.ProjectId, out _, out var refProject); - var undoManager = TryGetUndoManager(); - undoManager?.Add(new RemoveProjectReferenceUndoUnit( - this, projectId, projectReference.ProjectId)); - } + var vsProject = (VSProject)project.Object; + vsProject.References.AddProject(refProject); - private OleInterop.IOleUndoManager? TryGetUndoManager() - { - var documentTrackingService = this.Services.GetRequiredService(); - var documentId = documentTrackingService.TryGetActiveDocument() ?? documentTrackingService.GetVisibleDocuments().FirstOrDefault(); - if (documentId != null) - { - var composition = (IComponentModel)ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel)); - var exportProvider = composition.DefaultExportProvider; - var editorAdaptersService = exportProvider.GetExportedValue(); + var undoManager = TryGetUndoManager(); + undoManager?.Add(new RemoveProjectReferenceUndoUnit( + this, projectId, projectReference.ProjectId)); + } - return editorAdaptersService.TryGetUndoManager(this, documentId, CancellationToken.None); - } + private OleInterop.IOleUndoManager? TryGetUndoManager() + { + var documentTrackingService = this.Services.GetRequiredService(); + var documentId = documentTrackingService.TryGetActiveDocument() ?? documentTrackingService.GetVisibleDocuments().FirstOrDefault(); + if (documentId != null) + { + var composition = (IComponentModel)ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel)); + var exportProvider = composition.DefaultExportProvider; + var editorAdaptersService = exportProvider.GetExportedValue(); - return null; + return editorAdaptersService.TryGetUndoManager(this, documentId, CancellationToken.None); } - protected override void ApplyProjectReferenceRemoved( - ProjectId projectId, ProjectReference projectReference) + return null; + } + + protected override void ApplyProjectReferenceRemoved( + ProjectId projectId, ProjectReference projectReference) + { + if (projectId == null) { - if (projectId == null) - { - throw new ArgumentNullException(nameof(projectId)); - } + throw new ArgumentNullException(nameof(projectId)); + } - if (projectReference == null) - { - throw new ArgumentNullException(nameof(projectReference)); - } + if (projectReference == null) + { + throw new ArgumentNullException(nameof(projectReference)); + } - GetProjectData(projectId, out _, out var project); - GetProjectData(projectReference.ProjectId, out _, out var refProject); + GetProjectData(projectId, out _, out var project); + GetProjectData(projectReference.ProjectId, out _, out var refProject); - var vsProject = (VSProject)project.Object; - foreach (Reference reference in vsProject.References) + var vsProject = (VSProject)project.Object; + foreach (Reference reference in vsProject.References) + { + if (reference.SourceProject == refProject) { - if (reference.SourceProject == refProject) - { - reference.Remove(); - var undoManager = TryGetUndoManager(); - undoManager?.Add(new AddProjectReferenceUndoUnit(this, projectId, projectReference.ProjectId)); - } + reference.Remove(); + var undoManager = TryGetUndoManager(); + undoManager?.Add(new AddProjectReferenceUndoUnit(this, projectId, projectReference.ProjectId)); } } + } - protected override void ApplyDocumentAdded(DocumentInfo info, SourceText text) - => AddDocumentCore(info, text, TextDocumentKind.Document); + protected override void ApplyDocumentAdded(DocumentInfo info, SourceText text) + => AddDocumentCore(info, text, TextDocumentKind.Document); - protected override void ApplyAdditionalDocumentAdded(DocumentInfo info, SourceText text) - => AddDocumentCore(info, text, TextDocumentKind.AdditionalDocument); + protected override void ApplyAdditionalDocumentAdded(DocumentInfo info, SourceText text) + => AddDocumentCore(info, text, TextDocumentKind.AdditionalDocument); - protected override void ApplyAnalyzerConfigDocumentAdded(DocumentInfo info, SourceText text) - { - if (!TryAddEditorConfigToSolutionItems(info, text)) - AddDocumentCore(info, text, TextDocumentKind.AnalyzerConfigDocument); - } + protected override void ApplyAnalyzerConfigDocumentAdded(DocumentInfo info, SourceText text) + { + if (!TryAddEditorConfigToSolutionItems(info, text)) + AddDocumentCore(info, text, TextDocumentKind.AnalyzerConfigDocument); + } - private void AddDocumentCore(DocumentInfo info, SourceText initialText, TextDocumentKind documentKind) - { - GetProjectData(info.Id.ProjectId, out _, out var project); + private void AddDocumentCore(DocumentInfo info, SourceText initialText, TextDocumentKind documentKind) + { + GetProjectData(info.Id.ProjectId, out _, out var project); - // If the first namespace name matches the name of the project, then we don't want to - // generate a folder for that. The project is implicitly a folder with that name. - var folders = info.Folders.AsEnumerable(); - if (folders.FirstOrDefault() == project.Name) - { - folders = folders.Skip(1); - } + // If the first namespace name matches the name of the project, then we don't want to + // generate a folder for that. The project is implicitly a folder with that name. + var folders = info.Folders.AsEnumerable(); + if (folders.FirstOrDefault() == project.Name) + { + folders = folders.Skip(1); + } - folders = FilterFolderForProjectType(project, folders); + folders = FilterFolderForProjectType(project, folders); - if (IsWebsite(project)) - { - AddDocumentToFolder(project, info.Id, SpecializedCollections.SingletonEnumerable(AppCodeFolderName), info.Name, documentKind, initialText, info.FilePath); - } - else if (folders.Any()) - { - AddDocumentToFolder(project, info.Id, folders, info.Name, documentKind, initialText, info.FilePath); - } - else - { - AddDocumentToProject(project, info.Id, info.Name, documentKind, initialText, info.FilePath); - } + if (IsWebsite(project)) + { + AddDocumentToFolder(project, info.Id, SpecializedCollections.SingletonEnumerable(AppCodeFolderName), info.Name, documentKind, initialText, info.FilePath); + } + else if (folders.Any()) + { + AddDocumentToFolder(project, info.Id, folders, info.Name, documentKind, initialText, info.FilePath); + } + else + { + AddDocumentToProject(project, info.Id, info.Name, documentKind, initialText, info.FilePath); + } - var undoManager = TryGetUndoManager(); + var undoManager = TryGetUndoManager(); - switch (documentKind) - { - case TextDocumentKind.AdditionalDocument: - undoManager?.Add(new RemoveAdditionalDocumentUndoUnit(this, info.Id)); - break; + switch (documentKind) + { + case TextDocumentKind.AdditionalDocument: + undoManager?.Add(new RemoveAdditionalDocumentUndoUnit(this, info.Id)); + break; - case TextDocumentKind.AnalyzerConfigDocument: - undoManager?.Add(new RemoveAnalyzerConfigDocumentUndoUnit(this, info.Id)); - break; + case TextDocumentKind.AnalyzerConfigDocument: + undoManager?.Add(new RemoveAnalyzerConfigDocumentUndoUnit(this, info.Id)); + break; - case TextDocumentKind.Document: - undoManager?.Add(new RemoveDocumentUndoUnit(this, info.Id)); - break; + case TextDocumentKind.Document: + undoManager?.Add(new RemoveDocumentUndoUnit(this, info.Id)); + break; - default: - throw ExceptionUtilities.UnexpectedValue(documentKind); - } + default: + throw ExceptionUtilities.UnexpectedValue(documentKind); } + } - private static bool IsWebsite(EnvDTE.Project project) - => project.Kind == VsWebSite.PrjKind.prjKindVenusProject; + private static bool IsWebsite(EnvDTE.Project project) + => project.Kind == VsWebSite.PrjKind.prjKindVenusProject; - private static IEnumerable FilterFolderForProjectType(EnvDTE.Project project, IEnumerable folders) + private static IEnumerable FilterFolderForProjectType(EnvDTE.Project project, IEnumerable folders) + { + foreach (var folder in folders) { - foreach (var folder in folders) + var items = GetAllItems(project.ProjectItems); + var folderItem = items.FirstOrDefault(p => StringComparer.OrdinalIgnoreCase.Compare(p.Name, folder) == 0); + if (folderItem == null || folderItem.Kind != EnvDTE.Constants.vsProjectItemKindPhysicalFile) { - var items = GetAllItems(project.ProjectItems); - var folderItem = items.FirstOrDefault(p => StringComparer.OrdinalIgnoreCase.Compare(p.Name, folder) == 0); - if (folderItem == null || folderItem.Kind != EnvDTE.Constants.vsProjectItemKindPhysicalFile) - { - yield return folder; - } + yield return folder; } } + } - private static IEnumerable GetAllItems(ProjectItems projectItems) + private static IEnumerable GetAllItems(ProjectItems projectItems) + { + if (projectItems == null) { - if (projectItems == null) - { - return SpecializedCollections.EmptyEnumerable(); - } - - var items = projectItems.OfType(); - return items.Concat(items.SelectMany(i => GetAllItems(i.ProjectItems))); + return SpecializedCollections.EmptyEnumerable(); } + var items = projectItems.OfType(); + return items.Concat(items.SelectMany(i => GetAllItems(i.ProjectItems))); + } + #if false - protected override void AddExistingDocument(DocumentId documentId, string filePath, IEnumerable folders) + protected override void AddExistingDocument(DocumentId documentId, string filePath, IEnumerable folders) + { + IVsHierarchy hierarchy; + EnvDTE.Project project; + IVisualStudioHostProject hostProject; + GetProjectData(documentId.ProjectId, out hostProject, out hierarchy, out project); + + // If the first namespace name matches the name of the project, then we don't want to + // generate a folder for that. The project is implicitly a folder with that name. + if (folders.FirstOrDefault() == project.Name) { - IVsHierarchy hierarchy; - EnvDTE.Project project; - IVisualStudioHostProject hostProject; - GetProjectData(documentId.ProjectId, out hostProject, out hierarchy, out project); - - // If the first namespace name matches the name of the project, then we don't want to - // generate a folder for that. The project is implicitly a folder with that name. - if (folders.FirstOrDefault() == project.Name) - { - folders = folders.Skip(1); - } + folders = folders.Skip(1); + } - var name = Path.GetFileName(filePath); + var name = Path.GetFileName(filePath); - if (folders.Any()) - { - AddDocumentToFolder(hostProject, project, documentId, folders, name, SourceCodeKind.Regular, initialText: null, filePath: filePath); - } - else - { - AddDocumentToProject(hostProject, project, documentId, name, SourceCodeKind.Regular, initialText: null, filePath: filePath); - } + if (folders.Any()) + { + AddDocumentToFolder(hostProject, project, documentId, folders, name, SourceCodeKind.Regular, initialText: null, filePath: filePath); } + else + { + AddDocumentToProject(hostProject, project, documentId, name, SourceCodeKind.Regular, initialText: null, filePath: filePath); + } + } #endif - private void AddDocumentToProject( - EnvDTE.Project project, - DocumentId documentId, - string documentName, - TextDocumentKind documentKind, - SourceText? initialText = null, - string? filePath = null) + private void AddDocumentToProject( + EnvDTE.Project project, + DocumentId documentId, + string documentName, + TextDocumentKind documentKind, + SourceText? initialText = null, + string? filePath = null) + { + string? folderPath = null; + if (filePath == null && !project.TryGetFullPath(out folderPath)) { - string? folderPath = null; - if (filePath == null && !project.TryGetFullPath(out folderPath)) - { - // TODO(cyrusn): Throw an appropriate exception here. - throw new Exception(ServicesVSResources.Could_not_find_location_of_folder_on_disk); - } - - AddDocumentToProjectItems(project.ProjectItems, documentId, folderPath, documentName, initialText, filePath, documentKind); + // TODO(cyrusn): Throw an appropriate exception here. + throw new Exception(ServicesVSResources.Could_not_find_location_of_folder_on_disk); } - private void AddDocumentToFolder( - EnvDTE.Project project, - DocumentId documentId, - IEnumerable folders, - string documentName, - TextDocumentKind documentKind, - SourceText? initialText = null, - string? filePath = null) - { - var folder = project.FindOrCreateFolder(folders); + AddDocumentToProjectItems(project.ProjectItems, documentId, folderPath, documentName, initialText, filePath, documentKind); + } - string? folderPath = null; - if (filePath == null && !folder.TryGetFullPath(out folderPath)) - { - // TODO(cyrusn): Throw an appropriate exception here. - throw new Exception(ServicesVSResources.Could_not_find_location_of_folder_on_disk); - } + private void AddDocumentToFolder( + EnvDTE.Project project, + DocumentId documentId, + IEnumerable folders, + string documentName, + TextDocumentKind documentKind, + SourceText? initialText = null, + string? filePath = null) + { + var folder = project.FindOrCreateFolder(folders); - AddDocumentToProjectItems(folder.ProjectItems, documentId, folderPath, documentName, initialText, filePath, documentKind); + string? folderPath = null; + if (filePath == null && !folder.TryGetFullPath(out folderPath)) + { + // TODO(cyrusn): Throw an appropriate exception here. + throw new Exception(ServicesVSResources.Could_not_find_location_of_folder_on_disk); } - private bool TryAddEditorConfigToSolutionItems( - DocumentInfo documentInfo, - SourceText text) - { - // We're going to see if this is an .editorconfig being added at the solution level, and if so process it specially; so verify first that it is one - if (CurrentSolution.FilePath is null || documentInfo.FilePath is null) - return false; + AddDocumentToProjectItems(folder.ProjectItems, documentId, folderPath, documentName, initialText, filePath, documentKind); + } - if (PathUtilities.GetFileName(documentInfo.FilePath) != ".editorconfig") - return false; + private bool TryAddEditorConfigToSolutionItems( + DocumentInfo documentInfo, + SourceText text) + { + // We're going to see if this is an .editorconfig being added at the solution level, and if so process it specially; so verify first that it is one + if (CurrentSolution.FilePath is null || documentInfo.FilePath is null) + return false; - if (IOUtilities.PerformIO(() => File.Exists(documentInfo.FilePath))) - return false; + if (PathUtilities.GetFileName(documentInfo.FilePath) != ".editorconfig") + return false; - var solutionDirectory = PathUtilities.GetDirectoryName(this.CurrentSolution.FilePath); - if (solutionDirectory != PathUtilities.GetDirectoryName(documentInfo.FilePath)) - return false; + if (IOUtilities.PerformIO(() => File.Exists(documentInfo.FilePath))) + return false; - // Double check too that this isn't a case of the .csproj being in the same folder of the .sln, at which point it's reasonable - // just to add this to the project file. - if (PathUtilities.GetDirectoryName(CurrentSolution.GetProject(documentInfo.Id.ProjectId)?.FilePath) == solutionDirectory) - return false; + var solutionDirectory = PathUtilities.GetDirectoryName(this.CurrentSolution.FilePath); + if (solutionDirectory != PathUtilities.GetDirectoryName(documentInfo.FilePath)) + return false; - // All checks pass, so let's treat this special. - var dte = _threadingContext.JoinableTaskFactory.Run(() => _asyncServiceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory)); + // Double check too that this isn't a case of the .csproj being in the same folder of the .sln, at which point it's reasonable + // just to add this to the project file. + if (PathUtilities.GetDirectoryName(CurrentSolution.GetProject(documentInfo.Id.ProjectId)?.FilePath) == solutionDirectory) + return false; - const string SolutionItemsFolderName = "Solution Items"; + // All checks pass, so let's treat this special. + var dte = _threadingContext.JoinableTaskFactory.Run(() => _asyncServiceProvider.GetServiceAsync(_threadingContext.JoinableTaskFactory)); - var projects = dte.Solution.Projects.OfType(); - var solutionItemsFolder = projects.FirstOrDefault(static p => p.Kind == EnvDTE.Constants.vsProjectKindSolutionItems && p.Name == SolutionItemsFolderName); + const string SolutionItemsFolderName = "Solution Items"; - if (solutionItemsFolder != null) - { - foreach (ProjectItem projectItem in solutionItemsFolder.ProjectItems) - { - if (projectItem.Name == documentInfo.Name) - { - // It's already added to the solution folder, we just need to write the text and be done - using var writer = new StreamWriter(documentInfo.FilePath, append: false, encoding: text.Encoding ?? Encoding.UTF8); - text.Write(writer); - return true; - } - } - } - else + var projects = dte.Solution.Projects.OfType(); + var solutionItemsFolder = projects.FirstOrDefault(static p => p.Kind == EnvDTE.Constants.vsProjectKindSolutionItems && p.Name == SolutionItemsFolderName); + + if (solutionItemsFolder != null) + { + foreach (ProjectItem projectItem in solutionItemsFolder.ProjectItems) { - solutionItemsFolder = ((EnvDTE80.Solution2)dte.Solution).AddSolutionFolder(SolutionItemsFolderName); + if (projectItem.Name == documentInfo.Name) + { + // It's already added to the solution folder, we just need to write the text and be done + using var writer = new StreamWriter(documentInfo.FilePath, append: false, encoding: text.Encoding ?? Encoding.UTF8); + text.Write(writer); + return true; + } } + } + else + { + solutionItemsFolder = ((EnvDTE80.Solution2)dte.Solution).AddSolutionFolder(SolutionItemsFolderName); + } - AddDocumentToProjectItems( - solutionItemsFolder.ProjectItems, - documentInfo.Id, - folderPath: null, - documentInfo.Name, - text, - documentInfo.FilePath, - TextDocumentKind.AnalyzerConfigDocument); - dte.Solution.SaveAs(dte.Solution.FileName); + AddDocumentToProjectItems( + solutionItemsFolder.ProjectItems, + documentInfo.Id, + folderPath: null, + documentInfo.Name, + text, + documentInfo.FilePath, + TextDocumentKind.AnalyzerConfigDocument); + dte.Solution.SaveAs(dte.Solution.FileName); + + return true; + } - return true; + private void AddDocumentToProjectItems( + ProjectItems projectItems, + DocumentId documentId, + string? folderPath, + string documentName, + SourceText? initialText, + string? filePath, + TextDocumentKind documentKind) + { + if (filePath == null) + { + Contract.ThrowIfNull(folderPath, "If we didn't have a file path, then we expected a folder path to generate the file path from."); + var baseName = Path.GetFileNameWithoutExtension(documentName); + var extension = documentKind == TextDocumentKind.Document ? GetPreferredExtension(documentId) : Path.GetExtension(documentName); + var uniqueName = projectItems.GetUniqueName(baseName, extension); + filePath = Path.Combine(folderPath, uniqueName); } - private void AddDocumentToProjectItems( - ProjectItems projectItems, - DocumentId documentId, - string? folderPath, - string documentName, - SourceText? initialText, - string? filePath, - TextDocumentKind documentKind) + if (initialText != null) { - if (filePath == null) - { - Contract.ThrowIfNull(folderPath, "If we didn't have a file path, then we expected a folder path to generate the file path from."); - var baseName = Path.GetFileNameWithoutExtension(documentName); - var extension = documentKind == TextDocumentKind.Document ? GetPreferredExtension(documentId) : Path.GetExtension(documentName); - var uniqueName = projectItems.GetUniqueName(baseName, extension); - filePath = Path.Combine(folderPath, uniqueName); - } + using var writer = new StreamWriter(filePath, append: false, encoding: initialText.Encoding ?? Encoding.UTF8); + initialText.Write(writer); + } - if (initialText != null) - { - using var writer = new StreamWriter(filePath, append: false, encoding: initialText.Encoding ?? Encoding.UTF8); - initialText.Write(writer); - } + // TODO: restore document ID hinting -- we previously ensured that the AddFromFile will introduce the document ID being used here. + // (tracked by https://devdiv.visualstudio.com/DevDiv/_workitems/edit/677956) + projectItems.AddFromFile(filePath); + } - // TODO: restore document ID hinting -- we previously ensured that the AddFromFile will introduce the document ID being used here. - // (tracked by https://devdiv.visualstudio.com/DevDiv/_workitems/edit/677956) - projectItems.AddFromFile(filePath); + private void RemoveDocumentCore(DocumentId documentId, TextDocumentKind documentKind) + { + if (documentId == null) + { + throw new ArgumentNullException(nameof(documentId)); } - private void RemoveDocumentCore(DocumentId documentId, TextDocumentKind documentKind) + var document = this.CurrentSolution.GetTextDocument(documentId); + if (document != null) { - if (documentId == null) - { - throw new ArgumentNullException(nameof(documentId)); - } - - var document = this.CurrentSolution.GetTextDocument(documentId); - if (document != null) - { - var hierarchy = this.GetHierarchy(documentId.ProjectId); - Contract.ThrowIfNull(hierarchy, "Removing files from projects without hierarchies are not supported."); + var hierarchy = this.GetHierarchy(documentId.ProjectId); + Contract.ThrowIfNull(hierarchy, "Removing files from projects without hierarchies are not supported."); - var text = document.GetTextSynchronously(CancellationToken.None); + var text = document.GetTextSynchronously(CancellationToken.None); - Contract.ThrowIfNull(document.FilePath, "Removing files from projects that don't have file names are not supported."); - var itemId = hierarchy.TryGetItemId(document.FilePath); - if (itemId == (uint)VSConstants.VSITEMID.Nil) - { - // it is no longer part of the solution - return; - } + Contract.ThrowIfNull(document.FilePath, "Removing files from projects that don't have file names are not supported."); + var itemId = hierarchy.TryGetItemId(document.FilePath); + if (itemId == (uint)VSConstants.VSITEMID.Nil) + { + // it is no longer part of the solution + return; + } - var project = (IVsProject3)hierarchy; - project.RemoveItem(0, itemId, out _); + var project = (IVsProject3)hierarchy; + project.RemoveItem(0, itemId, out _); - var undoManager = TryGetUndoManager(); - var docInfo = CreateDocumentInfoWithoutText(document); + var undoManager = TryGetUndoManager(); + var docInfo = CreateDocumentInfoWithoutText(document); - switch (documentKind) - { - case TextDocumentKind.AdditionalDocument: - undoManager?.Add(new AddAdditionalDocumentUndoUnit(this, docInfo, text)); - break; + switch (documentKind) + { + case TextDocumentKind.AdditionalDocument: + undoManager?.Add(new AddAdditionalDocumentUndoUnit(this, docInfo, text)); + break; - case TextDocumentKind.AnalyzerConfigDocument: - undoManager?.Add(new AddAnalyzerConfigDocumentUndoUnit(this, docInfo, text)); - break; + case TextDocumentKind.AnalyzerConfigDocument: + undoManager?.Add(new AddAnalyzerConfigDocumentUndoUnit(this, docInfo, text)); + break; - case TextDocumentKind.Document: - undoManager?.Add(new AddDocumentUndoUnit(this, docInfo, text)); - break; + case TextDocumentKind.Document: + undoManager?.Add(new AddDocumentUndoUnit(this, docInfo, text)); + break; - default: - throw ExceptionUtilities.UnexpectedValue(documentKind); - } + default: + throw ExceptionUtilities.UnexpectedValue(documentKind); } } + } - protected override void ApplyDocumentRemoved(DocumentId documentId) - => RemoveDocumentCore(documentId, TextDocumentKind.Document); + protected override void ApplyDocumentRemoved(DocumentId documentId) + => RemoveDocumentCore(documentId, TextDocumentKind.Document); - protected override void ApplyAdditionalDocumentRemoved(DocumentId documentId) - => RemoveDocumentCore(documentId, TextDocumentKind.AdditionalDocument); + protected override void ApplyAdditionalDocumentRemoved(DocumentId documentId) + => RemoveDocumentCore(documentId, TextDocumentKind.AdditionalDocument); - protected override void ApplyAnalyzerConfigDocumentRemoved(DocumentId documentId) - => RemoveDocumentCore(documentId, TextDocumentKind.AnalyzerConfigDocument); + protected override void ApplyAnalyzerConfigDocumentRemoved(DocumentId documentId) + => RemoveDocumentCore(documentId, TextDocumentKind.AnalyzerConfigDocument); - public override void OpenDocument(DocumentId documentId, bool activate = true) - => OpenDocumentCore(documentId, activate); + public override void OpenDocument(DocumentId documentId, bool activate = true) + => OpenDocumentCore(documentId, activate); - public override void OpenAdditionalDocument(DocumentId documentId, bool activate = true) - => OpenDocumentCore(documentId, activate); + public override void OpenAdditionalDocument(DocumentId documentId, bool activate = true) + => OpenDocumentCore(documentId, activate); - public override void OpenAnalyzerConfigDocument(DocumentId documentId, bool activate = true) - => OpenDocumentCore(documentId, activate); + public override void OpenAnalyzerConfigDocument(DocumentId documentId, bool activate = true) + => OpenDocumentCore(documentId, activate); - public override void CloseDocument(DocumentId documentId) - => CloseDocumentCore(documentId); + public override void CloseDocument(DocumentId documentId) + => CloseDocumentCore(documentId); - public override void CloseAdditionalDocument(DocumentId documentId) - => CloseDocumentCore(documentId); + public override void CloseAdditionalDocument(DocumentId documentId) + => CloseDocumentCore(documentId); - public override void CloseAnalyzerConfigDocument(DocumentId documentId) - => CloseDocumentCore(documentId); + public override void CloseAnalyzerConfigDocument(DocumentId documentId) + => CloseDocumentCore(documentId); - public void OpenDocumentCore(DocumentId documentId, bool activate = true) + public void OpenDocumentCore(DocumentId documentId, bool activate = true) + { + if (documentId == null) { - if (documentId == null) - { - throw new ArgumentNullException(nameof(documentId)); - } - - if (!_foregroundObject.IsForeground()) - { - throw new InvalidOperationException(ServicesVSResources.This_workspace_only_supports_opening_documents_on_the_UI_thread); - } - - var document = this.CurrentSolution.GetTextDocument(documentId); - if (document != null) - { - OpenDocumentFromPath(document.FilePath, document.Project.Id, activate); - } + throw new ArgumentNullException(nameof(documentId)); } - internal void OpenDocumentFromPath(string? filePath, ProjectId projectId, bool activate = true) + if (!_foregroundObject.IsForeground()) { - if (TryGetFrame(filePath, projectId, out var frame)) - { - if (activate) - { - frame.Show(); - } - else - { - frame.ShowNoActivate(); - } - } + throw new InvalidOperationException(ServicesVSResources.This_workspace_only_supports_opening_documents_on_the_UI_thread); } - /// - /// Opens a file and retrieves the window frame. - /// - /// the file path of the file to open. - /// used to retrieve the IVsHierarchy to ensure the file is opened in a matching context. - /// the window frame. - /// - private bool TryGetFrame(string? filePath, ProjectId projectId, [NotNullWhen(returnValue: true)] out IVsWindowFrame? frame) + var document = this.CurrentSolution.GetTextDocument(documentId); + if (document != null) { - frame = null; - - if (filePath == null) - { - return false; - } - - var hierarchy = GetHierarchy(projectId); - var itemId = hierarchy?.TryGetItemId(filePath) ?? (uint)VSConstants.VSITEMID.Nil; - if (itemId == (uint)VSConstants.VSITEMID.Nil) - { - // If the ItemId is Nil, then IVsProject would not be able to open the - // document using its ItemId. Thus, we must use OpenDocumentViaProject, which only - // depends on the file path. - - var openDocumentService = ServiceProvider.GlobalProvider.GetServiceOnMainThread(); - return ErrorHandler.Succeeded(openDocumentService.OpenDocumentViaProject( - filePath, - VSConstants.LOGVIEWID.TextView_guid, - out _, - out _, - out _, - out frame)); - } - else - { - // If the ItemId is not Nil, then we should not call IVsUIShellDocument - // .OpenDocumentViaProject here because that simply takes a file path and opens the - // file within the context of the first project it finds. That would cause problems - // if the document we're trying to open is actually a linked file in another - // project. So, we get the project's hierarchy and open the document using its item - // ID. - - // It's conceivable that IVsHierarchy might not implement IVsProject. However, - // OpenDocumentViaProject itself relies upon this QI working, so it should be OK to - // use here. - - return hierarchy is IVsProject vsProject && - ErrorHandler.Succeeded(vsProject.OpenItem(itemId, VSConstants.LOGVIEWID.TextView_guid, s_docDataExisting_Unknown, out frame)); - } + OpenDocumentFromPath(document.FilePath, document.Project.Id, activate); } + } - public void CloseDocumentCore(DocumentId documentId) + internal void OpenDocumentFromPath(string? filePath, ProjectId projectId, bool activate = true) + { + if (TryGetFrame(filePath, projectId, out var frame)) { - if (documentId == null) + if (activate) { - throw new ArgumentNullException(nameof(documentId)); + frame.Show(); } - - if (this.IsDocumentOpen(documentId)) + else { - var filePath = this.GetFilePath(documentId); - if (filePath != null) - { - var openDocumentService = ServiceProvider.GlobalProvider.GetServiceOnMainThread(); - if (ErrorHandler.Succeeded(openDocumentService.IsDocumentOpen(null, 0, filePath, Guid.Empty, 0, out _, null, out var frame, out _))) - { - // TODO: do we need save argument for CloseDocument? - frame.CloseFrame((uint)__FRAMECLOSE.FRAMECLOSE_NoSave); - } - } + frame.ShowNoActivate(); } } + } - protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText newText) - => ApplyTextDocumentChange(documentId, newText); + /// + /// Opens a file and retrieves the window frame. + /// + /// the file path of the file to open. + /// used to retrieve the IVsHierarchy to ensure the file is opened in a matching context. + /// the window frame. + /// + private bool TryGetFrame(string? filePath, ProjectId projectId, [NotNullWhen(returnValue: true)] out IVsWindowFrame? frame) + { + frame = null; - protected override void ApplyAdditionalDocumentTextChanged(DocumentId documentId, SourceText newText) - => ApplyTextDocumentChange(documentId, newText); + if (filePath == null) + { + return false; + } - protected override void ApplyAnalyzerConfigDocumentTextChanged(DocumentId documentId, SourceText newText) - => ApplyTextDocumentChange(documentId, newText); + var hierarchy = GetHierarchy(projectId); + var itemId = hierarchy?.TryGetItemId(filePath) ?? (uint)VSConstants.VSITEMID.Nil; + if (itemId == (uint)VSConstants.VSITEMID.Nil) + { + // If the ItemId is Nil, then IVsProject would not be able to open the + // document using its ItemId. Thus, we must use OpenDocumentViaProject, which only + // depends on the file path. + + var openDocumentService = ServiceProvider.GlobalProvider.GetServiceOnMainThread(); + return ErrorHandler.Succeeded(openDocumentService.OpenDocumentViaProject( + filePath, + VSConstants.LOGVIEWID.TextView_guid, + out _, + out _, + out _, + out frame)); + } + else + { + // If the ItemId is not Nil, then we should not call IVsUIShellDocument + // .OpenDocumentViaProject here because that simply takes a file path and opens the + // file within the context of the first project it finds. That would cause problems + // if the document we're trying to open is actually a linked file in another + // project. So, we get the project's hierarchy and open the document using its item + // ID. + + // It's conceivable that IVsHierarchy might not implement IVsProject. However, + // OpenDocumentViaProject itself relies upon this QI working, so it should be OK to + // use here. + + return hierarchy is IVsProject vsProject && + ErrorHandler.Succeeded(vsProject.OpenItem(itemId, VSConstants.LOGVIEWID.TextView_guid, s_docDataExisting_Unknown, out frame)); + } + } - private void ApplyTextDocumentChange(DocumentId documentId, SourceText newText) + public void CloseDocumentCore(DocumentId documentId) + { + if (documentId == null) { - var containedDocument = ContainedDocument.TryGetContainedDocument(documentId); + throw new ArgumentNullException(nameof(documentId)); + } - if (containedDocument != null) - { - containedDocument.UpdateText(newText); - } - else + if (this.IsDocumentOpen(documentId)) + { + var filePath = this.GetFilePath(documentId); + if (filePath != null) { - if (IsDocumentOpen(documentId)) + var openDocumentService = ServiceProvider.GlobalProvider.GetServiceOnMainThread(); + if (ErrorHandler.Succeeded(openDocumentService.IsDocumentOpen(null, 0, filePath, Guid.Empty, 0, out _, null, out var frame, out _))) { - var textBuffer = this.CurrentSolution.GetTextDocument(documentId)!.GetTextSynchronously(CancellationToken.None).Container.TryGetTextBuffer(); - - if (textBuffer != null) - { - TextEditApplication.UpdateText(newText, textBuffer, EditOptions.DefaultMinimalChange); - return; - } + // TODO: do we need save argument for CloseDocument? + frame.CloseFrame((uint)__FRAMECLOSE.FRAMECLOSE_NoSave); } - - // The document wasn't open in a normal way, so invisible editor time - using var invisibleEditor = OpenInvisibleEditor(documentId); - TextEditApplication.UpdateText(newText, invisibleEditor.TextBuffer, EditOptions.None); } } + } - protected override void ApplyDocumentInfoChanged(DocumentId documentId, DocumentInfo updatedInfo) - { - var document = CurrentSolution.GetRequiredDocument(documentId); + protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText newText) + => ApplyTextDocumentChange(documentId, newText); - FailIfDocumentInfoChangesNotSupported(document, updatedInfo); + protected override void ApplyAdditionalDocumentTextChanged(DocumentId documentId, SourceText newText) + => ApplyTextDocumentChange(documentId, newText); - if (document.Name != updatedInfo.Name) - { - GetProjectData(updatedInfo.Id.ProjectId, out var _, out var dteProject); + protected override void ApplyAnalyzerConfigDocumentTextChanged(DocumentId documentId, SourceText newText) + => ApplyTextDocumentChange(documentId, newText); - if (document.FilePath == null) - { - FatalError.ReportAndCatch(new Exception("Attempting to change the information of a document without a file path.")); - return; - } + private void ApplyTextDocumentChange(DocumentId documentId, SourceText newText) + { + var containedDocument = ContainedDocument.TryGetContainedDocument(documentId); - var projectItemForDocument = dteProject.FindItemByPath(document.FilePath, StringComparer.OrdinalIgnoreCase); + if (containedDocument != null) + { + containedDocument.UpdateText(newText); + } + else + { + if (IsDocumentOpen(documentId)) + { + var textBuffer = this.CurrentSolution.GetTextDocument(documentId)!.GetTextSynchronously(CancellationToken.None).Container.TryGetTextBuffer(); - if (projectItemForDocument == null) + if (textBuffer != null) { - // TODO(https://github.com/dotnet/roslyn/issues/34276): - Debug.Fail("Attempting to change the name of a file in a Shared Project"); + TextEditApplication.UpdateText(newText, textBuffer, EditOptions.DefaultMinimalChange); return; } + } - // Must save the document first for things like Breakpoints to be preserved. - // WORKAROUND: Check if the document needs to be saved before calling save. - // Should remove the if below and just call save() once - // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1163405 - // is fixed - if (!projectItemForDocument.Saved) - { - projectItemForDocument.Save(); - } - - var uniqueName = projectItemForDocument.Collection - .GetUniqueNameIgnoringProjectItem( - projectItemForDocument, - Path.GetFileNameWithoutExtension(updatedInfo.Name), - Path.GetExtension(updatedInfo.Name)); - - // Get the current undoManager before any file renames/documentId changes happen - var undoManager = TryGetUndoManager(); + // The document wasn't open in a normal way, so invisible editor time + using var invisibleEditor = OpenInvisibleEditor(documentId); + TextEditApplication.UpdateText(newText, invisibleEditor.TextBuffer, EditOptions.None); + } + } - // By setting this property, Visual Studio will perform the file rename, which - // will cause the workspace's current solution to update and will fire the - // necessary workspace changed events. - projectItemForDocument.Name = uniqueName; + protected override void ApplyDocumentInfoChanged(DocumentId documentId, DocumentInfo updatedInfo) + { + var document = CurrentSolution.GetRequiredDocument(documentId); - if (projectItemForDocument.TryGetFullPath(out var newPath)) - { - undoManager?.Add(new RenameDocumentUndoUnit(this, uniqueName, document.Name, newPath)); - } - } - } + FailIfDocumentInfoChangesNotSupported(document, updatedInfo); - /// - /// The currently supports only a subset of - /// changes. - /// - private static void FailIfDocumentInfoChangesNotSupported(CodeAnalysis.Document document, DocumentInfo updatedInfo) + if (document.Name != updatedInfo.Name) { - if (document.SourceCodeKind != updatedInfo.SourceCodeKind) - { - throw new InvalidOperationException( - $"This Workspace does not support changing a document's {nameof(document.SourceCodeKind)}."); - } + GetProjectData(updatedInfo.Id.ProjectId, out var _, out var dteProject); - if (document.FilePath != updatedInfo.FilePath) + if (document.FilePath == null) { - throw new InvalidOperationException( - $"This Workspace does not support changing a document's {nameof(document.FilePath)}."); + FatalError.ReportAndCatch(new Exception("Attempting to change the information of a document without a file path.")); + return; } - if (document.Id != updatedInfo.Id) + var projectItemForDocument = dteProject.FindItemByPath(document.FilePath, StringComparer.OrdinalIgnoreCase); + + if (projectItemForDocument == null) { - throw new InvalidOperationException( - $"This Workspace does not support changing a document's {nameof(document.Id)}."); + // TODO(https://github.com/dotnet/roslyn/issues/34276): + Debug.Fail("Attempting to change the name of a file in a Shared Project"); + return; } - if (document.Folders != updatedInfo.Folders && !document.Folders.SequenceEqual(updatedInfo.Folders)) + // Must save the document first for things like Breakpoints to be preserved. + // WORKAROUND: Check if the document needs to be saved before calling save. + // Should remove the if below and just call save() once + // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1163405 + // is fixed + if (!projectItemForDocument.Saved) { - throw new InvalidOperationException( - $"This Workspace does not support changing a document's {nameof(document.Folders)}."); + projectItemForDocument.Save(); } - if (document.State.Attributes.IsGenerated != updatedInfo.IsGenerated) + var uniqueName = projectItemForDocument.Collection + .GetUniqueNameIgnoringProjectItem( + projectItemForDocument, + Path.GetFileNameWithoutExtension(updatedInfo.Name), + Path.GetExtension(updatedInfo.Name)); + + // Get the current undoManager before any file renames/documentId changes happen + var undoManager = TryGetUndoManager(); + + // By setting this property, Visual Studio will perform the file rename, which + // will cause the workspace's current solution to update and will fire the + // necessary workspace changed events. + projectItemForDocument.Name = uniqueName; + + if (projectItemForDocument.TryGetFullPath(out var newPath)) { - throw new InvalidOperationException( - $"This Workspace does not support changing a document's {nameof(document.State.Attributes.IsGenerated)} state."); + undoManager?.Add(new RenameDocumentUndoUnit(this, uniqueName, document.Name, newPath)); } } + } - private string GetPreferredExtension(DocumentId documentId) + /// + /// The currently supports only a subset of + /// changes. + /// + private static void FailIfDocumentInfoChangesNotSupported(CodeAnalysis.Document document, DocumentInfo updatedInfo) + { + if (document.SourceCodeKind != updatedInfo.SourceCodeKind) { - // No extension was provided. Pick a good one based on the type of host project. - return CurrentSolution.GetRequiredProject(documentId.ProjectId).Language switch - { - // TODO: uncomment when fixing https://github.com/dotnet/roslyn/issues/5325 - //return sourceCodeKind == SourceCodeKind.Regular ? ".cs" : ".csx"; - LanguageNames.CSharp => ".cs", - - // TODO: uncomment when fixing https://github.com/dotnet/roslyn/issues/5325 - //return sourceCodeKind == SourceCodeKind.Regular ? ".vb" : ".vbx"; - LanguageNames.VisualBasic => ".vb", - _ => throw new InvalidOperationException(), - }; + throw new InvalidOperationException( + $"This Workspace does not support changing a document's {nameof(document.SourceCodeKind)}."); } - public override IVsHierarchy? GetHierarchy(ProjectId projectId) + if (document.FilePath != updatedInfo.FilePath) { - // This doesn't take a lock since _projectToHierarchyMap is immutable - return _projectToHierarchyMap.GetValueOrDefault(projectId, defaultValue: null); + throw new InvalidOperationException( + $"This Workspace does not support changing a document's {nameof(document.FilePath)}."); } - internal override Guid GetProjectGuid(ProjectId projectId) + if (document.Id != updatedInfo.Id) { - // This doesn't take a lock since _projectToGuidMap is immutable - return _projectToGuidMap.GetValueOrDefault(projectId, defaultValue: Guid.Empty); + throw new InvalidOperationException( + $"This Workspace does not support changing a document's {nameof(document.Id)}."); } - internal string? TryGetDependencyNodeTargetIdentifier(ProjectId projectId) + if (document.Folders != updatedInfo.Folders && !document.Folders.SequenceEqual(updatedInfo.Folders)) { - return ProjectSystemProjectFactory.TryGetDependencyNodeTargetIdentifier(projectId); + throw new InvalidOperationException( + $"This Workspace does not support changing a document's {nameof(document.Folders)}."); } - internal override void SetDocumentContext(DocumentId documentId) + if (document.State.Attributes.IsGenerated != updatedInfo.IsGenerated) { - _foregroundObject.AssertIsForeground(); + throw new InvalidOperationException( + $"This Workspace does not support changing a document's {nameof(document.State.Attributes.IsGenerated)} state."); + } + } - // Note: this method does not actually call into any workspace code here to change the workspace's context. The assumption is updating the running document table or - // IVsHierarchies will raise the appropriate events which we are subscribed to. + private string GetPreferredExtension(DocumentId documentId) + { + // No extension was provided. Pick a good one based on the type of host project. + return CurrentSolution.GetRequiredProject(documentId.ProjectId).Language switch + { + // TODO: uncomment when fixing https://github.com/dotnet/roslyn/issues/5325 + //return sourceCodeKind == SourceCodeKind.Regular ? ".cs" : ".csx"; + LanguageNames.CSharp => ".cs", + + // TODO: uncomment when fixing https://github.com/dotnet/roslyn/issues/5325 + //return sourceCodeKind == SourceCodeKind.Regular ? ".vb" : ".vbx"; + LanguageNames.VisualBasic => ".vb", + _ => throw new InvalidOperationException(), + }; + } - var hierarchy = GetHierarchy(documentId.ProjectId); - if (hierarchy == null) - { - // If we don't have a hierarchy then there's nothing we can do - return; - } + public override IVsHierarchy? GetHierarchy(ProjectId projectId) + { + // This doesn't take a lock since _projectToHierarchyMap is immutable + return _projectToHierarchyMap.GetValueOrDefault(projectId, defaultValue: null); + } - // The hierarchy might be supporting multitargeting; in that case, let's update the context. Unfortunately the IVsHierarchies that support this - // don't necessarily let us read it first, so we have to fire-and-forget here. - string? projectSystemNameForProjectId = null; + internal override Guid GetProjectGuid(ProjectId projectId) + { + // This doesn't take a lock since _projectToGuidMap is immutable + return _projectToGuidMap.GetValueOrDefault(projectId, defaultValue: Guid.Empty); + } - using (_gate.DisposableWait()) - { - foreach (var (projectSystemName, projects) in _projectSystemNameToProjectsMap) - { - if (projects.Any(p => p.Id == documentId.ProjectId)) - { - projectSystemNameForProjectId = projectSystemName; - } - } - } + internal string? TryGetDependencyNodeTargetIdentifier(ProjectId projectId) + { + return ProjectSystemProjectFactory.TryGetDependencyNodeTargetIdentifier(projectId); + } - if (projectSystemNameForProjectId is null) - { - // Project must have been removed asynchronously - return; - } + internal override void SetDocumentContext(DocumentId documentId) + { + _foregroundObject.AssertIsForeground(); - // The hierarchy might be supporting multitargeting; in that case, let's update the context. Unfortunately the IVsHierarchies that support this - // don't necessarily let us read it first, so we have to fire-and-forget here. - hierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID8.VSHPROPID_ActiveIntellisenseProjectContext, projectSystemNameForProjectId); + // Note: this method does not actually call into any workspace code here to change the workspace's context. The assumption is updating the running document table or + // IVsHierarchies will raise the appropriate events which we are subscribed to. - var filePath = GetFilePath(documentId); - if (filePath == null) - { - return; - } + var hierarchy = GetHierarchy(documentId.ProjectId); + if (hierarchy == null) + { + // If we don't have a hierarchy then there's nothing we can do + return; + } - var itemId = hierarchy.TryGetItemId(filePath); - if (itemId != VSConstants.VSITEMID_NIL) + // The hierarchy might be supporting multitargeting; in that case, let's update the context. Unfortunately the IVsHierarchies that support this + // don't necessarily let us read it first, so we have to fire-and-forget here. + string? projectSystemNameForProjectId = null; + + using (_gate.DisposableWait()) + { + foreach (var (projectSystemName, projects) in _projectSystemNameToProjectsMap) { - // Is this owned by a shared asset project? If so, we need to put the shared asset project into the running document table, and need to set the - // current hierarchy as the active context of that shared hierarchy. This is kept as a loop that we do multiple times in the case that you - // have multiple pointers. This used to be the case for multitargeting projects, but that was now handled by setting the active context property - // above. Some project systems out there might still be supporting it, so we'll support it too. - while (SharedProjectUtilities.TryGetItemInSharedAssetsProject(hierarchy, itemId, out var sharedHierarchy, out var sharedItemId) && - hierarchy != sharedHierarchy) + if (projects.Any(p => p.Id == documentId.ProjectId)) { - // Ensure the shared context is set correctly - if (sharedHierarchy.GetActiveProjectContext() != hierarchy) - { - ErrorHandler.ThrowOnFailure(sharedHierarchy.SetActiveProjectContext(hierarchy)); - } - - // We now need to ensure the outer project is also set up - hierarchy = sharedHierarchy; - itemId = sharedItemId; + projectSystemNameForProjectId = projectSystemName; } } - - // Update the ownership of the file in the Running Document Table - var project = (IVsProject3)hierarchy; - project.TransferItem(filePath, filePath, punkWindowFrame: null); } - internal bool TryGetHierarchy(ProjectId projectId, [NotNullWhen(returnValue: true)] out IVsHierarchy? hierarchy) + if (projectSystemNameForProjectId is null) { - hierarchy = this.GetHierarchy(projectId); - return hierarchy != null; + // Project must have been removed asynchronously + return; } - protected override void Dispose(bool finalize) + // The hierarchy might be supporting multitargeting; in that case, let's update the context. Unfortunately the IVsHierarchies that support this + // don't necessarily let us read it first, so we have to fire-and-forget here. + hierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID8.VSHPROPID_ActiveIntellisenseProjectContext, projectSystemNameForProjectId); + + var filePath = GetFilePath(documentId); + if (filePath == null) { - if (!finalize) - { - _textBufferFactoryService.TextBufferCreated -= AddTextBufferCloneServiceToBuffer; - _projectionBufferFactoryService.ProjectionBufferCreated -= AddTextBufferCloneServiceToBuffer; + return; + } - if (_lazyExternalErrorDiagnosticUpdateSource.IsValueCreated) + var itemId = hierarchy.TryGetItemId(filePath); + if (itemId != VSConstants.VSITEMID_NIL) + { + // Is this owned by a shared asset project? If so, we need to put the shared asset project into the running document table, and need to set the + // current hierarchy as the active context of that shared hierarchy. This is kept as a loop that we do multiple times in the case that you + // have multiple pointers. This used to be the case for multitargeting projects, but that was now handled by setting the active context property + // above. Some project systems out there might still be supporting it, so we'll support it too. + while (SharedProjectUtilities.TryGetItemInSharedAssetsProject(hierarchy, itemId, out var sharedHierarchy, out var sharedItemId) && + hierarchy != sharedHierarchy) + { + // Ensure the shared context is set correctly + if (sharedHierarchy.GetActiveProjectContext() != hierarchy) { - _lazyExternalErrorDiagnosticUpdateSource.Value.Dispose(); + ErrorHandler.ThrowOnFailure(sharedHierarchy.SetActiveProjectContext(hierarchy)); } - } - base.Dispose(finalize); + // We now need to ensure the outer project is also set up + hierarchy = sharedHierarchy; + itemId = sharedItemId; + } } - public virtual void EnsureEditableDocuments(IEnumerable documents) - { - var queryEdit = (IVsQueryEditQuerySave2)ServiceProvider.GlobalProvider.GetService(typeof(SVsQueryEditQuerySave)); + // Update the ownership of the file in the Running Document Table + var project = (IVsProject3)hierarchy; + project.TransferItem(filePath, filePath, punkWindowFrame: null); + } - // make sure given document id actually exist in current solution and the file is marked as supporting modifications - // and actually has non null file path - var fileNames = documents.Select(GetFilePath).ToArray(); + internal bool TryGetHierarchy(ProjectId projectId, [NotNullWhen(returnValue: true)] out IVsHierarchy? hierarchy) + { + hierarchy = this.GetHierarchy(projectId); + return hierarchy != null; + } - // TODO: meditate about the flags we can pass to this and decide what is most appropriate for Roslyn - var result = queryEdit.QueryEditFiles( - rgfQueryEdit: 0, - cFiles: fileNames.Length, - rgpszMkDocuments: fileNames, - rgrgf: new uint[fileNames.Length], - rgFileInfo: new VSQEQS_FILE_ATTRIBUTE_DATA[fileNames.Length], - pfEditVerdict: out var editVerdict, - prgfMoreInfo: out var editResultFlags); + protected override void Dispose(bool finalize) + { + if (!finalize) + { + _textBufferFactoryService.TextBufferCreated -= AddTextBufferCloneServiceToBuffer; + _projectionBufferFactoryService.ProjectionBufferCreated -= AddTextBufferCloneServiceToBuffer; - if (ErrorHandler.Failed(result) || - editVerdict != (uint)tagVSQueryEditResult.QER_EditOK) + if (_lazyExternalErrorDiagnosticUpdateSource.IsValueCreated) { - throw new Exception("Unable to check out the files from source control."); + _lazyExternalErrorDiagnosticUpdateSource.Value.Dispose(); } + } - if ((editResultFlags & (uint)(tagVSQueryEditResultFlags2.QER_Changed | tagVSQueryEditResultFlags2.QER_Reloaded)) != 0) - { - throw new Exception("A file was reloaded during the source control checkout."); - } + base.Dispose(finalize); + } + + public virtual void EnsureEditableDocuments(IEnumerable documents) + { + var queryEdit = (IVsQueryEditQuerySave2)ServiceProvider.GlobalProvider.GetService(typeof(SVsQueryEditQuerySave)); + + // make sure given document id actually exist in current solution and the file is marked as supporting modifications + // and actually has non null file path + var fileNames = documents.Select(GetFilePath).ToArray(); + + // TODO: meditate about the flags we can pass to this and decide what is most appropriate for Roslyn + var result = queryEdit.QueryEditFiles( + rgfQueryEdit: 0, + cFiles: fileNames.Length, + rgpszMkDocuments: fileNames, + rgrgf: new uint[fileNames.Length], + rgFileInfo: new VSQEQS_FILE_ATTRIBUTE_DATA[fileNames.Length], + pfEditVerdict: out var editVerdict, + prgfMoreInfo: out var editResultFlags); + + if (ErrorHandler.Failed(result) || + editVerdict != (uint)tagVSQueryEditResult.QER_EditOK) + { + throw new Exception("Unable to check out the files from source control."); } - internal override bool CanAddProjectReference(ProjectId referencingProject, ProjectId referencedProject) + if ((editResultFlags & (uint)(tagVSQueryEditResultFlags2.QER_Changed | tagVSQueryEditResultFlags2.QER_Reloaded)) != 0) { - _foregroundObject.AssertIsForeground(); + throw new Exception("A file was reloaded during the source control checkout."); + } + } - if (!TryGetHierarchy(referencingProject, out var referencingHierarchy) || - !TryGetHierarchy(referencedProject, out var referencedHierarchy)) - { - // Couldn't even get a hierarchy for this project. So we have to assume - // that adding a reference is disallowed. - return false; - } + internal override bool CanAddProjectReference(ProjectId referencingProject, ProjectId referencedProject) + { + _foregroundObject.AssertIsForeground(); - // First we have to see if either project disallows the reference being added. - const int ContextFlags = (int)__VSQUERYFLAVORREFERENCESCONTEXT.VSQUERYFLAVORREFERENCESCONTEXT_RefreshReference; + if (!TryGetHierarchy(referencingProject, out var referencingHierarchy) || + !TryGetHierarchy(referencedProject, out var referencedHierarchy)) + { + // Couldn't even get a hierarchy for this project. So we have to assume + // that adding a reference is disallowed. + return false; + } - var canAddProjectReference = (uint)__VSREFERENCEQUERYRESULT.REFERENCE_UNKNOWN; - var canBeReferenced = (uint)__VSREFERENCEQUERYRESULT.REFERENCE_UNKNOWN; + // First we have to see if either project disallows the reference being added. + const int ContextFlags = (int)__VSQUERYFLAVORREFERENCESCONTEXT.VSQUERYFLAVORREFERENCESCONTEXT_RefreshReference; - if (referencingHierarchy is IVsProjectFlavorReferences3 referencingProjectFlavor3) - { - if (ErrorHandler.Failed(referencingProjectFlavor3.QueryAddProjectReferenceEx(referencedHierarchy, ContextFlags, out canAddProjectReference, out _))) - { - // Something went wrong even trying to see if the reference would be allowed. - // Assume it won't be allowed. - return false; - } + var canAddProjectReference = (uint)__VSREFERENCEQUERYRESULT.REFERENCE_UNKNOWN; + var canBeReferenced = (uint)__VSREFERENCEQUERYRESULT.REFERENCE_UNKNOWN; - if (canAddProjectReference == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY) - { - // Adding this project reference is not allowed. - return false; - } + if (referencingHierarchy is IVsProjectFlavorReferences3 referencingProjectFlavor3) + { + if (ErrorHandler.Failed(referencingProjectFlavor3.QueryAddProjectReferenceEx(referencedHierarchy, ContextFlags, out canAddProjectReference, out _))) + { + // Something went wrong even trying to see if the reference would be allowed. + // Assume it won't be allowed. + return false; } - if (referencedHierarchy is IVsProjectFlavorReferences3 referencedProjectFlavor3) + if (canAddProjectReference == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY) { - if (ErrorHandler.Failed(referencedProjectFlavor3.QueryCanBeReferencedEx(referencingHierarchy, ContextFlags, out canBeReferenced, out _))) - { - // Something went wrong even trying to see if the reference would be allowed. - // Assume it won't be allowed. - return false; - } - - if (canBeReferenced == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY) - { - // Adding this project reference is not allowed. - return false; - } + // Adding this project reference is not allowed. + return false; } + } - // Neither project denied the reference being added. At this point, if either project - // allows the reference to be added, and the other doesn't block it, then we can add - // the reference. - if (canAddProjectReference == (int)__VSREFERENCEQUERYRESULT.REFERENCE_ALLOW || - canBeReferenced == (int)__VSREFERENCEQUERYRESULT.REFERENCE_ALLOW) + if (referencedHierarchy is IVsProjectFlavorReferences3 referencedProjectFlavor3) + { + if (ErrorHandler.Failed(referencedProjectFlavor3.QueryCanBeReferencedEx(referencingHierarchy, ContextFlags, out canBeReferenced, out _))) { - return true; + // Something went wrong even trying to see if the reference would be allowed. + // Assume it won't be allowed. + return false; } - // In both directions things are still unknown. Fallback to the reference manager - // to make the determination here. - var referenceManager = (IVsReferenceManager)ServiceProvider.GlobalProvider.GetService(typeof(SVsReferenceManager)); - if (referenceManager == null) + if (canBeReferenced == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY) { - // Couldn't get the reference manager. Have to assume it's not allowed. + // Adding this project reference is not allowed. return false; } + } - // As long as the reference manager does not deny things, then we allow the - // reference to be added. - var result = referenceManager.QueryCanReferenceProject(referencingHierarchy, referencedHierarchy); - return result != (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY; + // Neither project denied the reference being added. At this point, if either project + // allows the reference to be added, and the other doesn't block it, then we can add + // the reference. + if (canAddProjectReference == (int)__VSREFERENCEQUERYRESULT.REFERENCE_ALLOW || + canBeReferenced == (int)__VSREFERENCEQUERYRESULT.REFERENCE_ALLOW) + { + return true; + } + + // In both directions things are still unknown. Fallback to the reference manager + // to make the determination here. + var referenceManager = (IVsReferenceManager)ServiceProvider.GlobalProvider.GetService(typeof(SVsReferenceManager)); + if (referenceManager == null) + { + // Couldn't get the reference manager. Have to assume it's not allowed. + return false; } - internal void RemoveProjectFromMaps(CodeAnalysis.Project project) + // As long as the reference manager does not deny things, then we allow the + // reference to be added. + var result = referenceManager.QueryCanReferenceProject(referencingHierarchy, referencedHierarchy); + return result != (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY; + } + + internal void RemoveProjectFromMaps(CodeAnalysis.Project project) + { + foreach (var (projectName, projects) in _projectSystemNameToProjectsMap) { - foreach (var (projectName, projects) in _projectSystemNameToProjectsMap) + if (projects.RemoveAll(p => p.Id == project.Id) > 0) { - if (projects.RemoveAll(p => p.Id == project.Id) > 0) + if (projects.Count == 0) { - if (projects.Count == 0) - { - _projectSystemNameToProjectsMap.Remove(projectName); - } - - break; + _projectSystemNameToProjectsMap.Remove(projectName); } - } - _projectToHierarchyMap = _projectToHierarchyMap.Remove(project.Id); - _projectToGuidMap = _projectToGuidMap.Remove(project.Id); + break; + } + } - ImmutableInterlocked.TryRemove(ref _projectToRuleSetFilePath, project.Id, out _); + _projectToHierarchyMap = _projectToHierarchyMap.Remove(project.Id); + _projectToGuidMap = _projectToGuidMap.Remove(project.Id); - // Try to update the UI context info. But cancel that work if we're shutting down. - _threadingContext.RunWithShutdownBlockAsync(async cancellationToken => - { - using var asyncToken = _workspaceListener.BeginAsyncOperation(nameof(RefreshProjectExistsUIContextForLanguageAsync)); - await RefreshProjectExistsUIContextForLanguageAsync(project.Language, cancellationToken).ConfigureAwait(false); - }); - } + ImmutableInterlocked.TryRemove(ref _projectToRuleSetFilePath, project.Id, out _); - internal async Task RefreshProjectExistsUIContextForLanguageAsync(string language, CancellationToken cancellationToken) + // Try to update the UI context info. But cancel that work if we're shutting down. + _threadingContext.RunWithShutdownBlockAsync(async cancellationToken => { - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, cancellationToken); + using var asyncToken = _workspaceListener.BeginAsyncOperation(nameof(RefreshProjectExistsUIContextForLanguageAsync)); + await RefreshProjectExistsUIContextForLanguageAsync(project.Language, cancellationToken).ConfigureAwait(false); + }); + } + + internal async Task RefreshProjectExistsUIContextForLanguageAsync(string language, CancellationToken cancellationToken) + { + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, cancellationToken); - var uiContext = _languageToProjectExistsUIContext.GetOrAdd( - language, - language => Services.GetLanguageServices(language).GetService()?.GetUIContext()); + var uiContext = _languageToProjectExistsUIContext.GetOrAdd( + language, + language => Services.GetLanguageServices(language).GetService()?.GetUIContext()); - // UIContexts can be "zombied" if UIContexts aren't supported because we're in a command line build or in - // other scenarios. - if (uiContext == null || uiContext.IsZombie) - return; + // UIContexts can be "zombied" if UIContexts aren't supported because we're in a command line build or in + // other scenarios. + if (uiContext == null || uiContext.IsZombie) + return; - // Note: it's safe to read CurrentSolution here outside of any sort of lock. We do all work here on the UI - // thread, so that acts as a natural ordering mechanism here. If, say, a BG piece of work was mutating this - // solution (either adding or removing a project) then that work will also have enqueued the next refresh - // operation on the UI thread. So we'll always eventually reach a fixed point where the task for that - // language will check the latest CurrentSolution we have and will set the IsActive bit accordingly. - uiContext.IsActive = this.CurrentSolution.Projects.Any(p => p.Language == language); - } + // Note: it's safe to read CurrentSolution here outside of any sort of lock. We do all work here on the UI + // thread, so that acts as a natural ordering mechanism here. If, say, a BG piece of work was mutating this + // solution (either adding or removing a project) then that work will also have enqueued the next refresh + // operation on the UI thread. So we'll always eventually reach a fixed point where the task for that + // language will check the latest CurrentSolution we have and will set the IsActive bit accordingly. + uiContext.IsActive = this.CurrentSolution.Projects.Any(p => p.Language == language); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/EventListener/WellKnownEventListeners.cs b/src/Workspaces/Core/Portable/Workspace/Host/EventListener/WellKnownEventListeners.cs index 205721200167d..ec8ada3750ae3 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/EventListener/WellKnownEventListeners.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/EventListener/WellKnownEventListeners.cs @@ -2,13 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.CodeAnalysis.Host +namespace Microsoft.CodeAnalysis.Host; + +/// +/// list of well known types +/// +internal static class WellKnownEventListeners { - /// - /// list of well known types - /// - internal static class WellKnownEventListeners - { - public const string Workspace = nameof(Workspace); - } + public const string Workspace = nameof(Workspace); } From e2885934ebad25ef5decc0da54ec60ed47dc3a55 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:23:43 -0800 Subject: [PATCH 047/151] file scoped --- .../Core/Interactive/InteractiveWorkspace.cs | 111 +++++++++--------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs index f4881e32c00b9..c1c0b68f199c3 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs @@ -9,83 +9,82 @@ using Microsoft.VisualStudio.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Interactive +namespace Microsoft.CodeAnalysis.Interactive; + +internal partial class InteractiveWorkspace : Workspace { - internal partial class InteractiveWorkspace : Workspace - { - private SourceTextContainer? _openTextContainer; - private DocumentId? _openDocumentId; + private SourceTextContainer? _openTextContainer; + private DocumentId? _openDocumentId; - internal InteractiveWorkspace(HostServices hostServices, IGlobalOptionService globalOptions) - : base(hostServices, WorkspaceKind.Interactive) - { - // register work coordinator for this workspace + internal InteractiveWorkspace(HostServices hostServices, IGlobalOptionService globalOptions) + : base(hostServices, WorkspaceKind.Interactive) + { + // register work coordinator for this workspace #if false - if (globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - Services.GetRequiredService().Register(this); - } -#endif + if (globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) + { + Services.GetRequiredService().Register(this); } +#endif + } - protected override void Dispose(bool finalize) - { - // workspace is going away. unregister this workspace from work coordinator + protected override void Dispose(bool finalize) + { + // workspace is going away. unregister this workspace from work coordinator #if false - Services.GetRequiredService().Unregister(this, blockingShutdown: true); + Services.GetRequiredService().Unregister(this, blockingShutdown: true); #endif - base.Dispose(finalize); - } + base.Dispose(finalize); + } - public override bool CanOpenDocuments - => true; + public override bool CanOpenDocuments + => true; - public override bool CanApplyChange(ApplyChangesKind feature) - => feature == ApplyChangesKind.ChangeDocument; + public override bool CanApplyChange(ApplyChangesKind feature) + => feature == ApplyChangesKind.ChangeDocument; - public void OpenDocument(DocumentId documentId, SourceTextContainer textContainer) + public void OpenDocument(DocumentId documentId, SourceTextContainer textContainer) + { + _openTextContainer = textContainer; + _openDocumentId = documentId; + OnDocumentOpened(documentId, textContainer); + } + + protected override void ApplyDocumentTextChanged(DocumentId document, SourceText newText) + { + if (_openDocumentId != document) { - _openTextContainer = textContainer; - _openDocumentId = documentId; - OnDocumentOpened(documentId, textContainer); + return; } - protected override void ApplyDocumentTextChanged(DocumentId document, SourceText newText) - { - if (_openDocumentId != document) - { - return; - } + Contract.ThrowIfNull(_openTextContainer); - Contract.ThrowIfNull(_openTextContainer); + ITextSnapshot appliedText; + using (var edit = _openTextContainer.GetTextBuffer().CreateEdit(EditOptions.DefaultMinimalChange, reiteratedVersionNumber: null, editTag: null)) + { + var oldText = _openTextContainer.CurrentText; + var changes = newText.GetTextChanges(oldText); - ITextSnapshot appliedText; - using (var edit = _openTextContainer.GetTextBuffer().CreateEdit(EditOptions.DefaultMinimalChange, reiteratedVersionNumber: null, editTag: null)) + foreach (var change in changes) { - var oldText = _openTextContainer.CurrentText; - var changes = newText.GetTextChanges(oldText); - - foreach (var change in changes) - { - edit.Replace(change.Span.Start, change.Span.Length, change.NewText); - } - - appliedText = edit.Apply(); + edit.Replace(change.Span.Start, change.Span.Length, change.NewText); } - OnDocumentTextChanged(document, appliedText.AsText(), PreservationMode.PreserveIdentity); + appliedText = edit.Apply(); } - /// - /// Closes all open documents and empties the solution but keeps all solution-level analyzers. - /// - public void ResetSolution() - { - ClearOpenDocuments(); + OnDocumentTextChanged(document, appliedText.AsText(), PreservationMode.PreserveIdentity); + } - var emptySolution = CreateSolution(SolutionId.CreateNewId("InteractiveSolution")); - SetCurrentSolution(solution => emptySolution.WithAnalyzerReferences(solution.AnalyzerReferences), WorkspaceChangeKind.SolutionCleared); - } + /// + /// Closes all open documents and empties the solution but keeps all solution-level analyzers. + /// + public void ResetSolution() + { + ClearOpenDocuments(); + + var emptySolution = CreateSolution(SolutionId.CreateNewId("InteractiveSolution")); + SetCurrentSolution(solution => emptySolution.WithAnalyzerReferences(solution.AnalyzerReferences), WorkspaceChangeKind.SolutionCleared); } } From 971e5aed4f558945dec15e91826f6a6d699efd57 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:24:45 -0800 Subject: [PATCH 048/151] file scoped --- .../Preview/AbstractPreviewFactoryService.cs | 1245 ++++++++--------- 1 file changed, 622 insertions(+), 623 deletions(-) diff --git a/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs b/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs index 67b9513cdf9c4..908a83820a713 100644 --- a/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs +++ b/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs @@ -29,796 +29,795 @@ using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview +namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview; + +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal abstract class AbstractPreviewFactoryService( + IThreadingContext threadingContext, + ITextBufferFactoryService textBufferFactoryService, + IContentTypeRegistryService contentTypeRegistryService, + IProjectionBufferFactoryService projectionBufferFactoryService, + EditorOptionsService editorOptionsService, + ITextDifferencingSelectorService differenceSelectorService, + IDifferenceBufferFactoryService differenceBufferService, + ITextDocumentFactoryService textDocumentFactoryService, + ITextViewRoleSet previewRoleSet) : IPreviewFactoryService + where TDifferenceViewer : IDifferenceViewer { - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - internal abstract class AbstractPreviewFactoryService( - IThreadingContext threadingContext, - ITextBufferFactoryService textBufferFactoryService, - IContentTypeRegistryService contentTypeRegistryService, - IProjectionBufferFactoryService projectionBufferFactoryService, - EditorOptionsService editorOptionsService, - ITextDifferencingSelectorService differenceSelectorService, - IDifferenceBufferFactoryService differenceBufferService, - ITextDocumentFactoryService textDocumentFactoryService, - ITextViewRoleSet previewRoleSet) : IPreviewFactoryService - where TDifferenceViewer : IDifferenceViewer + private const double DefaultZoomLevel = 0.75; + private readonly ITextViewRoleSet _previewRoleSet = previewRoleSet; + private readonly ITextBufferFactoryService _textBufferFactoryService = textBufferFactoryService; + private readonly IContentTypeRegistryService _contentTypeRegistryService = contentTypeRegistryService; + private readonly IProjectionBufferFactoryService _projectionBufferFactoryService = projectionBufferFactoryService; + private readonly EditorOptionsService _editorOptionsService = editorOptionsService; + private readonly ITextDifferencingSelectorService _differenceSelectorService = differenceSelectorService; + private readonly IDifferenceBufferFactoryService _differenceBufferService = differenceBufferService; + private readonly ITextDocumentFactoryService _textDocumentFactoryService = textDocumentFactoryService; + + protected readonly IThreadingContext ThreadingContext = threadingContext; + + public SolutionPreviewResult? GetSolutionPreviews(Solution oldSolution, Solution? newSolution, CancellationToken cancellationToken) + => GetSolutionPreviews(oldSolution, newSolution, DefaultZoomLevel, cancellationToken); + + public SolutionPreviewResult? GetSolutionPreviews(Solution oldSolution, Solution? newSolution, double zoomLevel, CancellationToken cancellationToken) { - private const double DefaultZoomLevel = 0.75; - private readonly ITextViewRoleSet _previewRoleSet = previewRoleSet; - private readonly ITextBufferFactoryService _textBufferFactoryService = textBufferFactoryService; - private readonly IContentTypeRegistryService _contentTypeRegistryService = contentTypeRegistryService; - private readonly IProjectionBufferFactoryService _projectionBufferFactoryService = projectionBufferFactoryService; - private readonly EditorOptionsService _editorOptionsService = editorOptionsService; - private readonly ITextDifferencingSelectorService _differenceSelectorService = differenceSelectorService; - private readonly IDifferenceBufferFactoryService _differenceBufferService = differenceBufferService; - private readonly ITextDocumentFactoryService _textDocumentFactoryService = textDocumentFactoryService; - - protected readonly IThreadingContext ThreadingContext = threadingContext; - - public SolutionPreviewResult? GetSolutionPreviews(Solution oldSolution, Solution? newSolution, CancellationToken cancellationToken) - => GetSolutionPreviews(oldSolution, newSolution, DefaultZoomLevel, cancellationToken); - - public SolutionPreviewResult? GetSolutionPreviews(Solution oldSolution, Solution? newSolution, double zoomLevel, CancellationToken cancellationToken) + cancellationToken.ThrowIfCancellationRequested(); + + // Note: The order in which previews are added to the below list is significant. + // Preview for a changed document is preferred over preview for changed references and so on. + var previewItems = new List(); + SolutionChangeSummary? changeSummary = null; + if (newSolution != null) { - cancellationToken.ThrowIfCancellationRequested(); + var solutionChanges = newSolution.GetChanges(oldSolution); + var ignoreUnchangeableDocuments = oldSolution.Workspace.IgnoreUnchangeableDocumentsWhenApplyingChanges; - // Note: The order in which previews are added to the below list is significant. - // Preview for a changed document is preferred over preview for changed references and so on. - var previewItems = new List(); - SolutionChangeSummary? changeSummary = null; - if (newSolution != null) + foreach (var projectChanges in solutionChanges.GetProjectChanges()) { - var solutionChanges = newSolution.GetChanges(oldSolution); - var ignoreUnchangeableDocuments = oldSolution.Workspace.IgnoreUnchangeableDocumentsWhenApplyingChanges; + cancellationToken.ThrowIfCancellationRequested(); + + var projectId = projectChanges.ProjectId; + var oldProject = projectChanges.OldProject; + var newProject = projectChanges.NewProject; - foreach (var projectChanges in solutionChanges.GetProjectChanges()) + // Exclude changes to unchangeable documents if they will be ignored when applied to workspace. + foreach (var documentId in projectChanges.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true, ignoreUnchangeableDocuments)) { cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => + await CreateChangedDocumentPreviewViewAsync(oldSolution.GetRequiredDocument(documentId), newSolution.GetRequiredDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); + } - var projectId = projectChanges.ProjectId; - var oldProject = projectChanges.OldProject; - var newProject = projectChanges.NewProject; - - // Exclude changes to unchangeable documents if they will be ignored when applied to workspace. - foreach (var documentId in projectChanges.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true, ignoreUnchangeableDocuments)) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => - await CreateChangedDocumentPreviewViewAsync(oldSolution.GetRequiredDocument(documentId), newSolution.GetRequiredDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetAddedDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => - await CreateAddedDocumentPreviewViewAsync(newSolution.GetRequiredDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetRemovedDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, documentId, async c => - await CreateRemovedDocumentPreviewViewAsync(oldSolution.GetRequiredDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetChangedAdditionalDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => - await CreateChangedAdditionalDocumentPreviewViewAsync(oldSolution.GetRequiredAdditionalDocument(documentId), newSolution.GetRequiredAdditionalDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetAddedAdditionalDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => - await CreateAddedAdditionalDocumentPreviewViewAsync(newSolution.GetRequiredAdditionalDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetRemovedAdditionalDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, documentId, async c => - await CreateRemovedAdditionalDocumentPreviewViewAsync(oldSolution.GetRequiredAdditionalDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetChangedAnalyzerConfigDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => - await CreateChangedAnalyzerConfigDocumentPreviewViewAsync(oldSolution.GetRequiredAnalyzerConfigDocument(documentId), newSolution.GetRequiredAnalyzerConfigDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetAddedAnalyzerConfigDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => - await CreateAddedAnalyzerConfigDocumentPreviewViewAsync(newSolution.GetRequiredAnalyzerConfigDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var documentId in projectChanges.GetRemovedAnalyzerConfigDocuments()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, documentId, async c => - await CreateRemovedAnalyzerConfigDocumentPreviewViewAsync(oldSolution.GetRequiredAnalyzerConfigDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); - } - - foreach (var metadataReference in projectChanges.GetAddedMetadataReferences()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, - string.Format(EditorFeaturesResources.Adding_reference_0_to_1, metadataReference.Display, oldProject.Name))); - } - - foreach (var metadataReference in projectChanges.GetRemovedMetadataReferences()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, - string.Format(EditorFeaturesResources.Removing_reference_0_from_1, metadataReference.Display, oldProject.Name))); - } - - foreach (var projectReference in projectChanges.GetAddedProjectReferences()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, - string.Format(EditorFeaturesResources.Adding_reference_0_to_1, newSolution.GetRequiredProject(projectReference.ProjectId).Name, oldProject.Name))); - } - - foreach (var projectReference in projectChanges.GetRemovedProjectReferences()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, - string.Format(EditorFeaturesResources.Removing_reference_0_from_1, oldSolution.GetRequiredProject(projectReference.ProjectId).Name, oldProject.Name))); - } - - foreach (var analyzer in projectChanges.GetAddedAnalyzerReferences()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, - string.Format(EditorFeaturesResources.Adding_analyzer_reference_0_to_1, analyzer.Display, oldProject.Name))); - } - - foreach (var analyzer in projectChanges.GetRemovedAnalyzerReferences()) - { - cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, - string.Format(EditorFeaturesResources.Removing_analyzer_reference_0_from_1, analyzer.Display, oldProject.Name))); - } + foreach (var documentId in projectChanges.GetAddedDocuments()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => + await CreateAddedDocumentPreviewViewAsync(newSolution.GetRequiredDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); } - foreach (var project in solutionChanges.GetAddedProjects()) + foreach (var documentId in projectChanges.GetRemovedDocuments()) { cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(project.Id, null, - string.Format(EditorFeaturesResources.Adding_project_0, project.Name))); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, documentId, async c => + await CreateRemovedDocumentPreviewViewAsync(oldSolution.GetRequiredDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); } - foreach (var project in solutionChanges.GetRemovedProjects()) + foreach (var documentId in projectChanges.GetChangedAdditionalDocuments()) { cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(project.Id, null, - string.Format(EditorFeaturesResources.Removing_project_0, project.Name))); + previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => + await CreateChangedAdditionalDocumentPreviewViewAsync(oldSolution.GetRequiredAdditionalDocument(documentId), newSolution.GetRequiredAdditionalDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); } - foreach (var projectChanges in solutionChanges.GetProjectChanges().Where(ProjectReferencesChanged)) + foreach (var documentId in projectChanges.GetAddedAdditionalDocuments()) { cancellationToken.ThrowIfCancellationRequested(); - previewItems.Add(new SolutionPreviewItem(projectChanges.OldProject.Id, null, - string.Format(EditorFeaturesResources.Changing_project_references_for_0, projectChanges.OldProject.Name))); + previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => + await CreateAddedAdditionalDocumentPreviewViewAsync(newSolution.GetRequiredAdditionalDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); } - changeSummary = new SolutionChangeSummary(oldSolution, newSolution, solutionChanges); - } + foreach (var documentId in projectChanges.GetRemovedAdditionalDocuments()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, documentId, async c => + await CreateRemovedAdditionalDocumentPreviewViewAsync(oldSolution.GetRequiredAdditionalDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); + } - return new SolutionPreviewResult(ThreadingContext, previewItems, changeSummary); - } + foreach (var documentId in projectChanges.GetChangedAnalyzerConfigDocuments()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => + await CreateChangedAnalyzerConfigDocumentPreviewViewAsync(oldSolution.GetRequiredAnalyzerConfigDocument(documentId), newSolution.GetRequiredAnalyzerConfigDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); + } - private bool ProjectReferencesChanged(ProjectChanges projectChanges) - { - var oldProjectReferences = projectChanges.OldProject.ProjectReferences.ToDictionary(r => r.ProjectId); - var newProjectReferences = projectChanges.NewProject.ProjectReferences.ToDictionary(r => r.ProjectId); + foreach (var documentId in projectChanges.GetAddedAnalyzerConfigDocuments()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(documentId.ProjectId, documentId, async c => + await CreateAddedAnalyzerConfigDocumentPreviewViewAsync(newSolution.GetRequiredAnalyzerConfigDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); + } - // These are the set of project reference that remained in the project. We don't care - // about project references that were added or removed. Those will already be reported. - var preservedProjectIds = oldProjectReferences.Keys.Intersect(newProjectReferences.Keys); + foreach (var documentId in projectChanges.GetRemovedAnalyzerConfigDocuments()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, documentId, async c => + await CreateRemovedAnalyzerConfigDocumentPreviewViewAsync(oldSolution.GetRequiredAnalyzerConfigDocument(documentId), zoomLevel, c).ConfigureAwaitRunInline())); + } - foreach (var projectId in preservedProjectIds) - { - var oldProjectReference = oldProjectReferences[projectId]; - var newProjectReference = newProjectReferences[projectId]; + foreach (var metadataReference in projectChanges.GetAddedMetadataReferences()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, + string.Format(EditorFeaturesResources.Adding_reference_0_to_1, metadataReference.Display, oldProject.Name))); + } + + foreach (var metadataReference in projectChanges.GetRemovedMetadataReferences()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, + string.Format(EditorFeaturesResources.Removing_reference_0_from_1, metadataReference.Display, oldProject.Name))); + } + + foreach (var projectReference in projectChanges.GetAddedProjectReferences()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, + string.Format(EditorFeaturesResources.Adding_reference_0_to_1, newSolution.GetRequiredProject(projectReference.ProjectId).Name, oldProject.Name))); + } - if (!oldProjectReference.Equals(newProjectReference)) + foreach (var projectReference in projectChanges.GetRemovedProjectReferences()) { - return true; + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, + string.Format(EditorFeaturesResources.Removing_reference_0_from_1, oldSolution.GetRequiredProject(projectReference.ProjectId).Name, oldProject.Name))); + } + + foreach (var analyzer in projectChanges.GetAddedAnalyzerReferences()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, + string.Format(EditorFeaturesResources.Adding_analyzer_reference_0_to_1, analyzer.Display, oldProject.Name))); + } + + foreach (var analyzer in projectChanges.GetRemovedAnalyzerReferences()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(oldProject.Id, null, + string.Format(EditorFeaturesResources.Removing_analyzer_reference_0_from_1, analyzer.Display, oldProject.Name))); } } - return false; + foreach (var project in solutionChanges.GetAddedProjects()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(project.Id, null, + string.Format(EditorFeaturesResources.Adding_project_0, project.Name))); + } + + foreach (var project in solutionChanges.GetRemovedProjects()) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(project.Id, null, + string.Format(EditorFeaturesResources.Removing_project_0, project.Name))); + } + + foreach (var projectChanges in solutionChanges.GetProjectChanges().Where(ProjectReferencesChanged)) + { + cancellationToken.ThrowIfCancellationRequested(); + previewItems.Add(new SolutionPreviewItem(projectChanges.OldProject.Id, null, + string.Format(EditorFeaturesResources.Changing_project_references_for_0, projectChanges.OldProject.Name))); + } + + changeSummary = new SolutionChangeSummary(oldSolution, newSolution, solutionChanges); } - public Task> CreateAddedDocumentPreviewViewAsync(Document document, CancellationToken cancellationToken) - => CreateAddedDocumentPreviewViewAsync(document, DefaultZoomLevel, cancellationToken); + return new SolutionPreviewResult(ThreadingContext, previewItems, changeSummary); + } + + private bool ProjectReferencesChanged(ProjectChanges projectChanges) + { + var oldProjectReferences = projectChanges.OldProject.ProjectReferences.ToDictionary(r => r.ProjectId); + var newProjectReferences = projectChanges.NewProject.ProjectReferences.ToDictionary(r => r.ProjectId); - private async ValueTask> CreateAddedDocumentPreviewViewCoreAsync(ITextBuffer newBuffer, ReferenceCountedDisposable workspace, TextDocument document, double zoomLevel, CancellationToken cancellationToken) + // These are the set of project reference that remained in the project. We don't care + // about project references that were added or removed. Those will already be reported. + var preservedProjectIds = oldProjectReferences.Keys.Intersect(newProjectReferences.Keys); + + foreach (var projectId in preservedProjectIds) { - // IProjectionBufferFactoryService is a Visual Studio API which is not documented as free-threaded - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + var oldProjectReference = oldProjectReferences[projectId]; + var newProjectReference = newProjectReferences[projectId]; + + if (!oldProjectReference.Equals(newProjectReference)) + { + return true; + } + } + + return false; + } + + public Task> CreateAddedDocumentPreviewViewAsync(Document document, CancellationToken cancellationToken) + => CreateAddedDocumentPreviewViewAsync(document, DefaultZoomLevel, cancellationToken); + + private async ValueTask> CreateAddedDocumentPreviewViewCoreAsync(ITextBuffer newBuffer, ReferenceCountedDisposable workspace, TextDocument document, double zoomLevel, CancellationToken cancellationToken) + { + // IProjectionBufferFactoryService is a Visual Studio API which is not documented as free-threaded + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var firstLine = string.Format(EditorFeaturesResources.Adding_0_to_1_with_content_colon, - document.Name, document.Project.Name); + var firstLine = string.Format(EditorFeaturesResources.Adding_0_to_1_with_content_colon, + document.Name, document.Project.Name); - var originalBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( - sourceSpans: [firstLine, "\r\n"], registryService: _contentTypeRegistryService); + var originalBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( + sourceSpans: [firstLine, "\r\n"], registryService: _contentTypeRegistryService); - var span = new SnapshotSpan(newBuffer.CurrentSnapshot, Span.FromBounds(0, newBuffer.CurrentSnapshot.Length)) - .CreateTrackingSpan(SpanTrackingMode.EdgeExclusive); - var changedBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( - sourceSpans: [firstLine, "\r\n", span], registryService: _contentTypeRegistryService); + var span = new SnapshotSpan(newBuffer.CurrentSnapshot, Span.FromBounds(0, newBuffer.CurrentSnapshot.Length)) + .CreateTrackingSpan(SpanTrackingMode.EdgeExclusive); + var changedBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( + sourceSpans: [firstLine, "\r\n", span], registryService: _contentTypeRegistryService); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateNewDifferenceViewerAsync(null, workspace, originalBuffer, changedBuffer, zoomLevel, cancellationToken); + return await CreateNewDifferenceViewerAsync(null, workspace, originalBuffer, changedBuffer, zoomLevel, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - private async Task> CreateAddedTextDocumentPreviewViewAsync( - TDocument document, - double zoomLevel, - Func> createBufferAsync, - CancellationToken cancellationToken) - where TDocument : TextDocument - { - // createBufferAsync must be called from the main thread - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + private async Task> CreateAddedTextDocumentPreviewViewAsync( + TDocument document, + double zoomLevel, + Func> createBufferAsync, + CancellationToken cancellationToken) + where TDocument : TextDocument + { + // createBufferAsync must be called from the main thread + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - var newBuffer = await createBufferAsync(document, cancellationToken); + var newBuffer = await createBufferAsync(document, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - // Create PreviewWorkspace around the buffer to be displayed in the diff preview - // so that all IDE services (colorizer, squiggles etc.) light up in this buffer. - using var rightWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(document.Project.Solution)); - rightWorkspace.Target.OpenDocument(document.Id, newBuffer.AsTextContainer()); + // Create PreviewWorkspace around the buffer to be displayed in the diff preview + // so that all IDE services (colorizer, squiggles etc.) light up in this buffer. + using var rightWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(document.Project.Solution)); + rightWorkspace.Target.OpenDocument(document.Id, newBuffer.AsTextContainer()); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateAddedDocumentPreviewViewCoreAsync(newBuffer, rightWorkspace, document, zoomLevel, cancellationToken); + return await CreateAddedDocumentPreviewViewCoreAsync(newBuffer, rightWorkspace, document, zoomLevel, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - public Task> CreateAddedDocumentPreviewViewAsync(Document document, double zoomLevel, CancellationToken cancellationToken) - { - return CreateAddedTextDocumentPreviewViewAsync( - document, zoomLevel, - createBufferAsync: (textDocument, cancellationToken) => CreateNewBufferAsync(textDocument, cancellationToken), - cancellationToken); - } + public Task> CreateAddedDocumentPreviewViewAsync(Document document, double zoomLevel, CancellationToken cancellationToken) + { + return CreateAddedTextDocumentPreviewViewAsync( + document, zoomLevel, + createBufferAsync: (textDocument, cancellationToken) => CreateNewBufferAsync(textDocument, cancellationToken), + cancellationToken); + } - public Task> CreateAddedAdditionalDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) - { - return CreateAddedTextDocumentPreviewViewAsync( - document, zoomLevel, - createBufferAsync: CreateNewPlainTextBufferAsync, - cancellationToken); - } + public Task> CreateAddedAdditionalDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) + { + return CreateAddedTextDocumentPreviewViewAsync( + document, zoomLevel, + createBufferAsync: CreateNewPlainTextBufferAsync, + cancellationToken); + } - public Task> CreateAddedAnalyzerConfigDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) - { - return CreateAddedTextDocumentPreviewViewAsync( - document, zoomLevel, - createBufferAsync: CreateNewPlainTextBufferAsync, - cancellationToken); - } + public Task> CreateAddedAnalyzerConfigDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) + { + return CreateAddedTextDocumentPreviewViewAsync( + document, zoomLevel, + createBufferAsync: CreateNewPlainTextBufferAsync, + cancellationToken); + } - public Task> CreateRemovedDocumentPreviewViewAsync(Document document, CancellationToken cancellationToken) - => CreateRemovedDocumentPreviewViewAsync(document, DefaultZoomLevel, cancellationToken); + public Task> CreateRemovedDocumentPreviewViewAsync(Document document, CancellationToken cancellationToken) + => CreateRemovedDocumentPreviewViewAsync(document, DefaultZoomLevel, cancellationToken); - private async ValueTask> CreateRemovedDocumentPreviewViewCoreAsync(ITextBuffer oldBuffer, ReferenceCountedDisposable workspace, TextDocument document, double zoomLevel, CancellationToken cancellationToken) - { - // IProjectionBufferFactoryService is a Visual Studio API which is not documented as free-threaded - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + private async ValueTask> CreateRemovedDocumentPreviewViewCoreAsync(ITextBuffer oldBuffer, ReferenceCountedDisposable workspace, TextDocument document, double zoomLevel, CancellationToken cancellationToken) + { + // IProjectionBufferFactoryService is a Visual Studio API which is not documented as free-threaded + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var firstLine = string.Format(EditorFeaturesResources.Removing_0_from_1_with_content_colon, - document.Name, document.Project.Name); + var firstLine = string.Format(EditorFeaturesResources.Removing_0_from_1_with_content_colon, + document.Name, document.Project.Name); - var span = new SnapshotSpan(oldBuffer.CurrentSnapshot, Span.FromBounds(0, oldBuffer.CurrentSnapshot.Length)) - .CreateTrackingSpan(SpanTrackingMode.EdgeExclusive); - var originalBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( - sourceSpans: [firstLine, "\r\n", span], registryService: _contentTypeRegistryService); + var span = new SnapshotSpan(oldBuffer.CurrentSnapshot, Span.FromBounds(0, oldBuffer.CurrentSnapshot.Length)) + .CreateTrackingSpan(SpanTrackingMode.EdgeExclusive); + var originalBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( + sourceSpans: [firstLine, "\r\n", span], registryService: _contentTypeRegistryService); - var changedBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( - sourceSpans: [firstLine, "\r\n"], registryService: _contentTypeRegistryService); + var changedBuffer = _projectionBufferFactoryService.CreatePreviewProjectionBuffer( + sourceSpans: [firstLine, "\r\n"], registryService: _contentTypeRegistryService); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateNewDifferenceViewerAsync(workspace, null, originalBuffer, changedBuffer, zoomLevel, cancellationToken); + return await CreateNewDifferenceViewerAsync(workspace, null, originalBuffer, changedBuffer, zoomLevel, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - private async Task> CreateRemovedTextDocumentPreviewViewAsync( - TDocument document, - double zoomLevel, - Func> createBufferAsync, - CancellationToken cancellationToken) - where TDocument : TextDocument - { - // createBufferAsync must be called from the main thread - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - // Note: We don't use the original buffer that is associated with oldDocument - // (and possibly open in the editor) for oldBuffer below. This is because oldBuffer - // will be used inside a projection buffer inside our inline diff preview below - // and platform's implementation currently has a bug where projection buffers - // are being leaked. This leak means that if we use the original buffer that is - // currently visible in the editor here, the projection buffer span calculation - // would be triggered every time user changes some code in this buffer (even though - // the diff view would long have been dismissed by the time user edits the code) - // resulting in crashes. Instead we create a new buffer from the same content. - // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. + private async Task> CreateRemovedTextDocumentPreviewViewAsync( + TDocument document, + double zoomLevel, + Func> createBufferAsync, + CancellationToken cancellationToken) + where TDocument : TextDocument + { + // createBufferAsync must be called from the main thread + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + // Note: We don't use the original buffer that is associated with oldDocument + // (and possibly open in the editor) for oldBuffer below. This is because oldBuffer + // will be used inside a projection buffer inside our inline diff preview below + // and platform's implementation currently has a bug where projection buffers + // are being leaked. This leak means that if we use the original buffer that is + // currently visible in the editor here, the projection buffer span calculation + // would be triggered every time user changes some code in this buffer (even though + // the diff view would long have been dismissed by the time user edits the code) + // resulting in crashes. Instead we create a new buffer from the same content. + // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - var oldBuffer = await createBufferAsync(document, cancellationToken); + var oldBuffer = await createBufferAsync(document, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - // Create PreviewWorkspace around the buffer to be displayed in the diff preview - // so that all IDE services (colorizer, squiggles etc.) light up in this buffer. - using var leftWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(document.Project.Solution)); - leftWorkspace.Target.OpenDocument(document.Id, oldBuffer.AsTextContainer()); + // Create PreviewWorkspace around the buffer to be displayed in the diff preview + // so that all IDE services (colorizer, squiggles etc.) light up in this buffer. + using var leftWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(document.Project.Solution)); + leftWorkspace.Target.OpenDocument(document.Id, oldBuffer.AsTextContainer()); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateRemovedDocumentPreviewViewCoreAsync(oldBuffer, leftWorkspace, document, zoomLevel, cancellationToken); + return await CreateRemovedDocumentPreviewViewCoreAsync(oldBuffer, leftWorkspace, document, zoomLevel, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - public Task> CreateRemovedDocumentPreviewViewAsync(Document document, double zoomLevel, CancellationToken cancellationToken) - { - return CreateRemovedTextDocumentPreviewViewAsync( - document, zoomLevel, - createBufferAsync: (textDocument, cancellationToken) => CreateNewBufferAsync(textDocument, cancellationToken), - cancellationToken); - } + public Task> CreateRemovedDocumentPreviewViewAsync(Document document, double zoomLevel, CancellationToken cancellationToken) + { + return CreateRemovedTextDocumentPreviewViewAsync( + document, zoomLevel, + createBufferAsync: (textDocument, cancellationToken) => CreateNewBufferAsync(textDocument, cancellationToken), + cancellationToken); + } - public Task> CreateRemovedAdditionalDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) - { - return CreateRemovedTextDocumentPreviewViewAsync( - document, zoomLevel, - createBufferAsync: CreateNewPlainTextBufferAsync, - cancellationToken); - } + public Task> CreateRemovedAdditionalDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) + { + return CreateRemovedTextDocumentPreviewViewAsync( + document, zoomLevel, + createBufferAsync: CreateNewPlainTextBufferAsync, + cancellationToken); + } - public Task> CreateRemovedAnalyzerConfigDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) - { - return CreateRemovedTextDocumentPreviewViewAsync( - document, zoomLevel, - createBufferAsync: CreateNewPlainTextBufferAsync, - cancellationToken); - } + public Task> CreateRemovedAnalyzerConfigDocumentPreviewViewAsync(TextDocument document, double zoomLevel, CancellationToken cancellationToken) + { + return CreateRemovedTextDocumentPreviewViewAsync( + document, zoomLevel, + createBufferAsync: CreateNewPlainTextBufferAsync, + cancellationToken); + } - public Task?> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) - => CreateChangedDocumentPreviewViewAsync(oldDocument, newDocument, DefaultZoomLevel, cancellationToken); + public Task?> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) + => CreateChangedDocumentPreviewViewAsync(oldDocument, newDocument, DefaultZoomLevel, cancellationToken); - public async Task?> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, double zoomLevel, CancellationToken cancellationToken) - { - // CreateNewBufferAsync must be called from the main thread - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - // Note: We don't use the original buffer that is associated with oldDocument - // (and currently open in the editor) for oldBuffer below. This is because oldBuffer - // will be used inside a projection buffer inside our inline diff preview below - // and platform's implementation currently has a bug where projection buffers - // are being leaked. This leak means that if we use the original buffer that is - // currently visible in the editor here, the projection buffer span calculation - // would be triggered every time user changes some code in this buffer (even though - // the diff view would long have been dismissed by the time user edits the code) - // resulting in crashes. Instead we create a new buffer from the same content. - // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. + public async Task?> CreateChangedDocumentPreviewViewAsync(Document oldDocument, Document newDocument, double zoomLevel, CancellationToken cancellationToken) + { + // CreateNewBufferAsync must be called from the main thread + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + // Note: We don't use the original buffer that is associated with oldDocument + // (and currently open in the editor) for oldBuffer below. This is because oldBuffer + // will be used inside a projection buffer inside our inline diff preview below + // and platform's implementation currently has a bug where projection buffers + // are being leaked. This leak means that if we use the original buffer that is + // currently visible in the editor here, the projection buffer span calculation + // would be triggered every time user changes some code in this buffer (even though + // the diff view would long have been dismissed by the time user edits the code) + // resulting in crashes. Instead we create a new buffer from the same content. + // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - var oldBuffer = await CreateNewBufferAsync(oldDocument, cancellationToken); - var newBuffer = await CreateNewBufferAsync(newDocument, cancellationToken); + var oldBuffer = await CreateNewBufferAsync(oldDocument, cancellationToken); + var newBuffer = await CreateNewBufferAsync(newDocument, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - // Convert the diffs to be line based. - // Compute the diffs between the old text and the new. - var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); + // Convert the diffs to be line based. + // Compute the diffs between the old text and the new. + var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); - // Need to show the spans in the right that are different. - // We also need to show the spans that are in conflict. - var originalSpans = GetOriginalSpans(diffResult, cancellationToken); - var changedSpans = GetChangedSpans(diffResult, cancellationToken); - string? description = null; - NormalizedSpanCollection allSpans; + // Need to show the spans in the right that are different. + // We also need to show the spans that are in conflict. + var originalSpans = GetOriginalSpans(diffResult, cancellationToken); + var changedSpans = GetChangedSpans(diffResult, cancellationToken); + string? description = null; + NormalizedSpanCollection allSpans; - if (newDocument.SupportsSyntaxTree) - { + if (newDocument.SupportsSyntaxTree) + { #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - var newRoot = await newDocument.GetRequiredSyntaxRootAsync(cancellationToken); + var newRoot = await newDocument.GetRequiredSyntaxRootAsync(cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - var conflictNodes = newRoot.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); - var conflictSpans = conflictNodes.Select(n => n.Span.ToSpan()).ToList(); - var conflictDescriptions = conflictNodes.SelectMany(n => n.GetAnnotations(ConflictAnnotation.Kind)) - .Select(a => $"❌ {ConflictAnnotation.GetDescription(a)}") - .Distinct(); - - var warningNodes = newRoot.GetAnnotatedNodesAndTokens(WarningAnnotation.Kind); - var warningSpans = warningNodes.Select(n => n.Span.ToSpan()).ToList(); - var warningDescriptions = warningNodes.SelectMany(n => n.GetAnnotations(WarningAnnotation.Kind)) - .Select(a => $"⚠ {WarningAnnotation.GetDescription(a)}") - .Distinct(); - - var suppressDiagnosticsNodes = newRoot.GetAnnotatedNodesAndTokens(SuppressDiagnosticsAnnotation.Kind); - var suppressDiagnosticsSpans = suppressDiagnosticsNodes.Select(n => n.Span.ToSpan()).ToList(); - AttachAnnotationsToBuffer(newBuffer, conflictSpans, warningSpans, suppressDiagnosticsSpans); - - description = conflictSpans.Count == 0 && warningSpans.Count == 0 - ? null - : string.Join(Environment.NewLine, conflictDescriptions.Concat(warningDescriptions)); - allSpans = new NormalizedSpanCollection(conflictSpans.Concat(warningSpans).Concat(changedSpans)); - } - else - { - allSpans = new NormalizedSpanCollection(changedSpans); - } + var conflictNodes = newRoot.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind); + var conflictSpans = conflictNodes.Select(n => n.Span.ToSpan()).ToList(); + var conflictDescriptions = conflictNodes.SelectMany(n => n.GetAnnotations(ConflictAnnotation.Kind)) + .Select(a => $"❌ {ConflictAnnotation.GetDescription(a)}") + .Distinct(); + + var warningNodes = newRoot.GetAnnotatedNodesAndTokens(WarningAnnotation.Kind); + var warningSpans = warningNodes.Select(n => n.Span.ToSpan()).ToList(); + var warningDescriptions = warningNodes.SelectMany(n => n.GetAnnotations(WarningAnnotation.Kind)) + .Select(a => $"⚠ {WarningAnnotation.GetDescription(a)}") + .Distinct(); + + var suppressDiagnosticsNodes = newRoot.GetAnnotatedNodesAndTokens(SuppressDiagnosticsAnnotation.Kind); + var suppressDiagnosticsSpans = suppressDiagnosticsNodes.Select(n => n.Span.ToSpan()).ToList(); + AttachAnnotationsToBuffer(newBuffer, conflictSpans, warningSpans, suppressDiagnosticsSpans); + + description = conflictSpans.Count == 0 && warningSpans.Count == 0 + ? null + : string.Join(Environment.NewLine, conflictDescriptions.Concat(warningDescriptions)); + allSpans = new NormalizedSpanCollection(conflictSpans.Concat(warningSpans).Concat(changedSpans)); + } + else + { + allSpans = new NormalizedSpanCollection(changedSpans); + } - var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); - var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, allSpans, cancellationToken); - if (!originalLineSpans.Any()) - { - // This means that we have no differences (likely because of conflicts). - // In such cases, use the same spans for the left (old) buffer as the right (new) buffer. - originalLineSpans = changedLineSpans; - } + var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); + var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, allSpans, cancellationToken); + if (!originalLineSpans.Any()) + { + // This means that we have no differences (likely because of conflicts). + // In such cases, use the same spans for the left (old) buffer as the right (new) buffer. + originalLineSpans = changedLineSpans; + } - // Create PreviewWorkspaces around the buffers to be displayed on the left and right - // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. - // - // Performance: Replace related documents to oldBuffer and newBuffer in these workspaces with the - // relating SourceText. This prevents cascading forks as taggers call to - // GetOpenTextDocumentInCurrentContextWithChanges would eventually wind up - // calling Solution.WithDocumentText using the related ids. - var leftSolution = oldDocument.Project.Solution; - var allLeftIds = leftSolution.GetRelatedDocumentIds(oldDocument.Id); - leftSolution = leftSolution.WithDocumentText(allLeftIds, oldBuffer.AsTextContainer().CurrentText, PreservationMode.PreserveIdentity); + // Create PreviewWorkspaces around the buffers to be displayed on the left and right + // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. + // + // Performance: Replace related documents to oldBuffer and newBuffer in these workspaces with the + // relating SourceText. This prevents cascading forks as taggers call to + // GetOpenTextDocumentInCurrentContextWithChanges would eventually wind up + // calling Solution.WithDocumentText using the related ids. + var leftSolution = oldDocument.Project.Solution; + var allLeftIds = leftSolution.GetRelatedDocumentIds(oldDocument.Id); + leftSolution = leftSolution.WithDocumentText(allLeftIds, oldBuffer.AsTextContainer().CurrentText, PreservationMode.PreserveIdentity); - using var leftWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(leftSolution)); - leftWorkspace.Target.OpenDocument(oldDocument.Id, oldBuffer.AsTextContainer()); + using var leftWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(leftSolution)); + leftWorkspace.Target.OpenDocument(oldDocument.Id, oldBuffer.AsTextContainer()); - var rightSolution = newDocument.Project.Solution; - var allRightIds = rightSolution.GetRelatedDocumentIds(newDocument.Id); - rightSolution = rightSolution.WithDocumentText(allRightIds, newBuffer.AsTextContainer().CurrentText, PreservationMode.PreserveIdentity); + var rightSolution = newDocument.Project.Solution; + var allRightIds = rightSolution.GetRelatedDocumentIds(newDocument.Id); + rightSolution = rightSolution.WithDocumentText(allRightIds, newBuffer.AsTextContainer().CurrentText, PreservationMode.PreserveIdentity); - using var rightWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(rightSolution)); - rightWorkspace.Target.OpenDocument(newDocument.Id, newBuffer.AsTextContainer()); + using var rightWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(rightSolution)); + rightWorkspace.Target.OpenDocument(newDocument.Id, newBuffer.AsTextContainer()); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateChangedDocumentViewAsync( - oldBuffer, newBuffer, description, originalLineSpans, changedLineSpans, - leftWorkspace, rightWorkspace, zoomLevel, cancellationToken); + return await CreateChangedDocumentViewAsync( + oldBuffer, newBuffer, description, originalLineSpans, changedLineSpans, + leftWorkspace, rightWorkspace, zoomLevel, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - // NOTE: We are only sharing this code between additional documents and analyzer config documents, - // which are essentially plain text documents. Regular source documents need special handling - // and hence have a different implementation. - private async Task?> CreateChangedAdditionalOrAnalyzerConfigDocumentPreviewViewAsync( - TextDocument oldDocument, - TextDocument newDocument, - double zoomLevel, - CancellationToken cancellationToken) - { - Debug.Assert(oldDocument.Kind is TextDocumentKind.AdditionalDocument or TextDocumentKind.AnalyzerConfigDocument); - - // openTextDocument must be called from the main thread - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - // Note: We don't use the original buffer that is associated with oldDocument - // (and currently open in the editor) for oldBuffer below. This is because oldBuffer - // will be used inside a projection buffer inside our inline diff preview below - // and platform's implementation currently has a bug where projection buffers - // are being leaked. This leak means that if we use the original buffer that is - // currently visible in the editor here, the projection buffer span calculation - // would be triggered every time user changes some code in this buffer (even though - // the diff view would long have been dismissed by the time user edits the code) - // resulting in crashes. Instead we create a new buffer from the same content. - // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. + // NOTE: We are only sharing this code between additional documents and analyzer config documents, + // which are essentially plain text documents. Regular source documents need special handling + // and hence have a different implementation. + private async Task?> CreateChangedAdditionalOrAnalyzerConfigDocumentPreviewViewAsync( + TextDocument oldDocument, + TextDocument newDocument, + double zoomLevel, + CancellationToken cancellationToken) + { + Debug.Assert(oldDocument.Kind is TextDocumentKind.AdditionalDocument or TextDocumentKind.AnalyzerConfigDocument); + + // openTextDocument must be called from the main thread + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + // Note: We don't use the original buffer that is associated with oldDocument + // (and currently open in the editor) for oldBuffer below. This is because oldBuffer + // will be used inside a projection buffer inside our inline diff preview below + // and platform's implementation currently has a bug where projection buffers + // are being leaked. This leak means that if we use the original buffer that is + // currently visible in the editor here, the projection buffer span calculation + // would be triggered every time user changes some code in this buffer (even though + // the diff view would long have been dismissed by the time user edits the code) + // resulting in crashes. Instead we create a new buffer from the same content. + // TODO: We could use ITextBufferCloneService instead here to clone the original buffer. #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - var oldBuffer = await CreateNewPlainTextBufferAsync(oldDocument, cancellationToken); - var newBuffer = await CreateNewPlainTextBufferAsync(newDocument, cancellationToken); + var oldBuffer = await CreateNewPlainTextBufferAsync(oldDocument, cancellationToken); + var newBuffer = await CreateNewPlainTextBufferAsync(newDocument, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - // Convert the diffs to be line based. - // Compute the diffs between the old text and the new. - var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); + // Convert the diffs to be line based. + // Compute the diffs between the old text and the new. + var diffResult = ComputeEditDifferences(oldDocument, newDocument, cancellationToken); - // Need to show the spans in the right that are different. - var originalSpans = GetOriginalSpans(diffResult, cancellationToken); - var changedSpans = GetChangedSpans(diffResult, cancellationToken); + // Need to show the spans in the right that are different. + var originalSpans = GetOriginalSpans(diffResult, cancellationToken); + var changedSpans = GetChangedSpans(diffResult, cancellationToken); - var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); - var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, changedSpans, cancellationToken); + var originalLineSpans = CreateLineSpans(oldBuffer.CurrentSnapshot, originalSpans, cancellationToken); + var changedLineSpans = CreateLineSpans(newBuffer.CurrentSnapshot, changedSpans, cancellationToken); - // TODO: Why aren't we attaching conflict / warning annotations here like we do for regular documents above? + // TODO: Why aren't we attaching conflict / warning annotations here like we do for regular documents above? - // Create PreviewWorkspaces around the buffers to be displayed on the left and right - // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. - using var leftWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(oldDocument.Project.Solution)); - leftWorkspace.Target.OpenDocument(oldDocument.Id, oldBuffer.AsTextContainer()); + // Create PreviewWorkspaces around the buffers to be displayed on the left and right + // so that all IDE services (colorizer, squiggles etc.) light up in these buffers. + using var leftWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(oldDocument.Project.Solution)); + leftWorkspace.Target.OpenDocument(oldDocument.Id, oldBuffer.AsTextContainer()); - using var rightWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(newDocument.Project.Solution)); - rightWorkspace.Target.OpenDocument(newDocument.Id, newBuffer.AsTextContainer()); + using var rightWorkspace = new ReferenceCountedDisposable(new PreviewWorkspace(newDocument.Project.Solution)); + rightWorkspace.Target.OpenDocument(newDocument.Id, newBuffer.AsTextContainer()); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateChangedDocumentViewAsync( - oldBuffer, newBuffer, description: null, originalLineSpans, changedLineSpans, - leftWorkspace, rightWorkspace, zoomLevel, cancellationToken); + return await CreateChangedDocumentViewAsync( + oldBuffer, newBuffer, description: null, originalLineSpans, changedLineSpans, + leftWorkspace, rightWorkspace, zoomLevel, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - public Task?> CreateChangedAdditionalDocumentPreviewViewAsync(TextDocument oldDocument, TextDocument newDocument, double zoomLevel, CancellationToken cancellationToken) - { - return CreateChangedAdditionalOrAnalyzerConfigDocumentPreviewViewAsync( - oldDocument, newDocument, zoomLevel, cancellationToken); - } + public Task?> CreateChangedAdditionalDocumentPreviewViewAsync(TextDocument oldDocument, TextDocument newDocument, double zoomLevel, CancellationToken cancellationToken) + { + return CreateChangedAdditionalOrAnalyzerConfigDocumentPreviewViewAsync( + oldDocument, newDocument, zoomLevel, cancellationToken); + } - public Task?> CreateChangedAnalyzerConfigDocumentPreviewViewAsync(TextDocument oldDocument, TextDocument newDocument, double zoomLevel, CancellationToken cancellationToken) - { - return CreateChangedAdditionalOrAnalyzerConfigDocumentPreviewViewAsync( - oldDocument, newDocument, zoomLevel, cancellationToken); - } + public Task?> CreateChangedAnalyzerConfigDocumentPreviewViewAsync(TextDocument oldDocument, TextDocument newDocument, double zoomLevel, CancellationToken cancellationToken) + { + return CreateChangedAdditionalOrAnalyzerConfigDocumentPreviewViewAsync( + oldDocument, newDocument, zoomLevel, cancellationToken); + } - private async ValueTask?> CreateChangedDocumentViewAsync(ITextBuffer oldBuffer, ITextBuffer newBuffer, string? description, - List originalSpans, List changedSpans, ReferenceCountedDisposable leftWorkspace, ReferenceCountedDisposable rightWorkspace, - double zoomLevel, CancellationToken cancellationToken) + private async ValueTask?> CreateChangedDocumentViewAsync(ITextBuffer oldBuffer, ITextBuffer newBuffer, string? description, + List originalSpans, List changedSpans, ReferenceCountedDisposable leftWorkspace, ReferenceCountedDisposable rightWorkspace, + double zoomLevel, CancellationToken cancellationToken) + { + if (!(originalSpans.Any() && changedSpans.Any())) { - if (!(originalSpans.Any() && changedSpans.Any())) - { - // Both line spans must be non-empty. Otherwise, below projection buffer factory API call will throw. - // So if either is empty (signaling that there are no changes to preview in the document), then we bail out. - // This can happen in cases where the user has already applied the fix and light bulb has already been dismissed, - // but platform hasn't cancelled the preview operation yet. Since the light bulb has already been dismissed at - // this point, the preview that we return will never be displayed to the user. So returning null here is harmless. - - // TODO: understand how this can even happen. The diff input is stable -- we shouldn't be depending on some sort of - // state that could change underneath us. If we know the file changed, how would we discover here it didn't? - return null; - } + // Both line spans must be non-empty. Otherwise, below projection buffer factory API call will throw. + // So if either is empty (signaling that there are no changes to preview in the document), then we bail out. + // This can happen in cases where the user has already applied the fix and light bulb has already been dismissed, + // but platform hasn't cancelled the preview operation yet. Since the light bulb has already been dismissed at + // this point, the preview that we return will never be displayed to the user. So returning null here is harmless. + + // TODO: understand how this can even happen. The diff input is stable -- we shouldn't be depending on some sort of + // state that could change underneath us. If we know the file changed, how would we discover here it didn't? + return null; + } - // IProjectionBufferFactoryService is a Visual Studio API which is not documented as free-threaded - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var originalBuffer = _projectionBufferFactoryService.CreateProjectionBufferWithoutIndentation( - _contentTypeRegistryService, - _editorOptionsService.Factory.GlobalOptions, - oldBuffer.CurrentSnapshot, - "...", - description, - originalSpans.ToArray()); - - var changedBuffer = _projectionBufferFactoryService.CreateProjectionBufferWithoutIndentation( - _contentTypeRegistryService, - _editorOptionsService.Factory.GlobalOptions, - newBuffer.CurrentSnapshot, - "...", - description, - changedSpans.ToArray()); + // IProjectionBufferFactoryService is a Visual Studio API which is not documented as free-threaded + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var originalBuffer = _projectionBufferFactoryService.CreateProjectionBufferWithoutIndentation( + _contentTypeRegistryService, + _editorOptionsService.Factory.GlobalOptions, + oldBuffer.CurrentSnapshot, + "...", + description, + originalSpans.ToArray()); + + var changedBuffer = _projectionBufferFactoryService.CreateProjectionBufferWithoutIndentation( + _contentTypeRegistryService, + _editorOptionsService.Factory.GlobalOptions, + newBuffer.CurrentSnapshot, + "...", + description, + changedSpans.ToArray()); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateNewDifferenceViewerAsync(leftWorkspace, rightWorkspace, originalBuffer, changedBuffer, zoomLevel, cancellationToken); + return await CreateNewDifferenceViewerAsync(leftWorkspace, rightWorkspace, originalBuffer, changedBuffer, zoomLevel, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - private static void AttachAnnotationsToBuffer( - ITextBuffer newBuffer, IEnumerable conflictSpans, IEnumerable warningSpans, IEnumerable suppressDiagnosticsSpans) - { - // Attach the spans to the buffer. - newBuffer.Properties.AddProperty(PredefinedPreviewTaggerKeys.ConflictSpansKey, new NormalizedSnapshotSpanCollection(newBuffer.CurrentSnapshot, conflictSpans)); - newBuffer.Properties.AddProperty(PredefinedPreviewTaggerKeys.WarningSpansKey, new NormalizedSnapshotSpanCollection(newBuffer.CurrentSnapshot, warningSpans)); - newBuffer.Properties.AddProperty(PredefinedPreviewTaggerKeys.SuppressDiagnosticsSpansKey, new NormalizedSnapshotSpanCollection(newBuffer.CurrentSnapshot, suppressDiagnosticsSpans)); - } + private static void AttachAnnotationsToBuffer( + ITextBuffer newBuffer, IEnumerable conflictSpans, IEnumerable warningSpans, IEnumerable suppressDiagnosticsSpans) + { + // Attach the spans to the buffer. + newBuffer.Properties.AddProperty(PredefinedPreviewTaggerKeys.ConflictSpansKey, new NormalizedSnapshotSpanCollection(newBuffer.CurrentSnapshot, conflictSpans)); + newBuffer.Properties.AddProperty(PredefinedPreviewTaggerKeys.WarningSpansKey, new NormalizedSnapshotSpanCollection(newBuffer.CurrentSnapshot, warningSpans)); + newBuffer.Properties.AddProperty(PredefinedPreviewTaggerKeys.SuppressDiagnosticsSpansKey, new NormalizedSnapshotSpanCollection(newBuffer.CurrentSnapshot, suppressDiagnosticsSpans)); + } - private async ValueTask CreateNewBufferAsync(Document document, CancellationToken cancellationToken) - { - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + private async ValueTask CreateNewBufferAsync(Document document, CancellationToken cancellationToken) + { + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var contentTypeService = document.GetRequiredLanguageService(); - var contentType = contentTypeService.GetDefaultContentType(); + var contentTypeService = document.GetRequiredLanguageService(); + var contentType = contentTypeService.GetDefaultContentType(); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateTextBufferCoreAsync(document, contentType, cancellationToken); + return await CreateTextBufferCoreAsync(document, contentType, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - private async ValueTask CreateNewPlainTextBufferAsync(TextDocument document, CancellationToken cancellationToken) - { - // ITextBufferFactoryService is a Visual Studio API which is not documented as free-threaded - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + private async ValueTask CreateNewPlainTextBufferAsync(TextDocument document, CancellationToken cancellationToken) + { + // ITextBufferFactoryService is a Visual Studio API which is not documented as free-threaded + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var contentType = _textBufferFactoryService.TextContentType; + var contentType = _textBufferFactoryService.TextContentType; #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - return await CreateTextBufferCoreAsync(document, contentType, cancellationToken); + return await CreateTextBufferCoreAsync(document, contentType, cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - } + } - private async ValueTask CreateTextBufferCoreAsync(TextDocument document, IContentType? contentType, CancellationToken cancellationToken) - { - ThreadingContext.ThrowIfNotOnUIThread(); + private async ValueTask CreateTextBufferCoreAsync(TextDocument document, IContentType? contentType, CancellationToken cancellationToken) + { + ThreadingContext.ThrowIfNotOnUIThread(); #pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task (containing method uses JTF) - var text = await document.State.GetTextAsync(cancellationToken); + var text = await document.State.GetTextAsync(cancellationToken); #pragma warning restore CA2007 // Consider calling ConfigureAwait on the awaited task - var buffer = _textBufferFactoryService.CreateTextBuffer(text.ToString(), contentType); + var buffer = _textBufferFactoryService.CreateTextBuffer(text.ToString(), contentType); - // Associate buffer with a text document with random file path to satisfy extensibility points expecting absolute file path. - _textDocumentFactoryService.CreateTextDocument(buffer, Path.GetTempFileName()); + // Associate buffer with a text document with random file path to satisfy extensibility points expecting absolute file path. + _textDocumentFactoryService.CreateTextDocument(buffer, Path.GetTempFileName()); - return buffer; - } + return buffer; + } - protected abstract IDifferenceViewerPreview CreateDifferenceViewerPreview(TDifferenceViewer viewer); - protected abstract Task CreateDifferenceViewAsync(IDifferenceBuffer diffBuffer, ITextViewRoleSet previewRoleSet, DifferenceViewMode mode, double zoomLevel, CancellationToken cancellationToken); + protected abstract IDifferenceViewerPreview CreateDifferenceViewerPreview(TDifferenceViewer viewer); + protected abstract Task CreateDifferenceViewAsync(IDifferenceBuffer diffBuffer, ITextViewRoleSet previewRoleSet, DifferenceViewMode mode, double zoomLevel, CancellationToken cancellationToken); - private async ValueTask> CreateNewDifferenceViewerAsync( - ReferenceCountedDisposable? leftWorkspace, ReferenceCountedDisposable? rightWorkspace, - IProjectionBuffer originalBuffer, IProjectionBuffer changedBuffer, - double zoomLevel, CancellationToken cancellationToken) - { - // IWpfDifferenceViewerFactoryService is a Visual Studio API which is not documented as free-threaded - await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + private async ValueTask> CreateNewDifferenceViewerAsync( + ReferenceCountedDisposable? leftWorkspace, ReferenceCountedDisposable? rightWorkspace, + IProjectionBuffer originalBuffer, IProjectionBuffer changedBuffer, + double zoomLevel, CancellationToken cancellationToken) + { + // IWpfDifferenceViewerFactoryService is a Visual Studio API which is not documented as free-threaded + await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - // leftWorkspace can be null if the change is adding a document. - // rightWorkspace can be null if the change is removing a document. - // However both leftWorkspace and rightWorkspace can't be null at the same time. - Contract.ThrowIfTrue((leftWorkspace == null) && (rightWorkspace == null)); + // leftWorkspace can be null if the change is adding a document. + // rightWorkspace can be null if the change is removing a document. + // However both leftWorkspace and rightWorkspace can't be null at the same time. + Contract.ThrowIfTrue((leftWorkspace == null) && (rightWorkspace == null)); - var diffBuffer = _differenceBufferService.CreateDifferenceBuffer( - originalBuffer, changedBuffer, - new StringDifferenceOptions(), disableEditing: true); + var diffBuffer = _differenceBufferService.CreateDifferenceBuffer( + originalBuffer, changedBuffer, + new StringDifferenceOptions(), disableEditing: true); - var mode = leftWorkspace == null ? DifferenceViewMode.RightViewOnly : - rightWorkspace == null ? DifferenceViewMode.LeftViewOnly : - DifferenceViewMode.Inline; + var mode = leftWorkspace == null ? DifferenceViewMode.RightViewOnly : + rightWorkspace == null ? DifferenceViewMode.LeftViewOnly : + DifferenceViewMode.Inline; - var diffViewer = await CreateDifferenceViewAsync(diffBuffer, _previewRoleSet, mode, zoomLevel, cancellationToken).ConfigureAwait(true); + var diffViewer = await CreateDifferenceViewAsync(diffBuffer, _previewRoleSet, mode, zoomLevel, cancellationToken).ConfigureAwait(true); - // Claim ownership of the workspace references - leftWorkspace = leftWorkspace?.TryAddReference(); - rightWorkspace = rightWorkspace?.TryAddReference(); + // Claim ownership of the workspace references + leftWorkspace = leftWorkspace?.TryAddReference(); + rightWorkspace = rightWorkspace?.TryAddReference(); - diffViewer.Closed += (s, e) => - { - // Workaround Editor bug. The editor has an issue where they sometimes crash when - // trying to apply changes to projection buffer. So, when the user actually invokes - // a SuggestedAction we may then edit a text buffer, which the editor will then - // try to propagate through the projections we have here over that buffer. To ensure - // that that doesn't happen, we clear out the projections first so that this crash - // won't happen. - originalBuffer.DeleteSpans(0, originalBuffer.CurrentSnapshot.SpanCount); - changedBuffer.DeleteSpans(0, changedBuffer.CurrentSnapshot.SpanCount); - - leftWorkspace?.Dispose(); - leftWorkspace = null; - - rightWorkspace?.Dispose(); - rightWorkspace = null; - }; + diffViewer.Closed += (s, e) => + { + // Workaround Editor bug. The editor has an issue where they sometimes crash when + // trying to apply changes to projection buffer. So, when the user actually invokes + // a SuggestedAction we may then edit a text buffer, which the editor will then + // try to propagate through the projections we have here over that buffer. To ensure + // that that doesn't happen, we clear out the projections first so that this crash + // won't happen. + originalBuffer.DeleteSpans(0, originalBuffer.CurrentSnapshot.SpanCount); + changedBuffer.DeleteSpans(0, changedBuffer.CurrentSnapshot.SpanCount); + + leftWorkspace?.Dispose(); + leftWorkspace = null; + + rightWorkspace?.Dispose(); + rightWorkspace = null; + }; #if false - if (_editorOptionsService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - leftWorkspace?.Target.EnableSolutionCrawler(); - rightWorkspace?.Target.EnableSolutionCrawler(); - } + if (_editorOptionsService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) + { + leftWorkspace?.Target.EnableSolutionCrawler(); + rightWorkspace?.Target.EnableSolutionCrawler(); + } #endif - return CreateDifferenceViewerPreview(diffViewer); - } + return CreateDifferenceViewerPreview(diffViewer); + } + + private static List CreateLineSpans(ITextSnapshot textSnapshot, NormalizedSpanCollection allSpans, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var result = new List(); - private static List CreateLineSpans(ITextSnapshot textSnapshot, NormalizedSpanCollection allSpans, CancellationToken cancellationToken) + foreach (var span in allSpans) { cancellationToken.ThrowIfCancellationRequested(); - var result = new List(); + var lineSpan = GetLineSpan(textSnapshot, span); + MergeLineSpans(result, lineSpan); + } - foreach (var span in allSpans) - { - cancellationToken.ThrowIfCancellationRequested(); + return result; + } - var lineSpan = GetLineSpan(textSnapshot, span); - MergeLineSpans(result, lineSpan); - } + // Find the lines that surround the span of the difference. Try to expand the span to + // include both the previous and next lines so that we can show more context to the + // user. + private static LineSpan GetLineSpan( + ITextSnapshot snapshot, + Span span) + { + var startLine = snapshot.GetLineNumberFromPosition(span.Start); + var endLine = snapshot.GetLineNumberFromPosition(span.End); - return result; + if (startLine > 0) + { + startLine--; } - // Find the lines that surround the span of the difference. Try to expand the span to - // include both the previous and next lines so that we can show more context to the - // user. - private static LineSpan GetLineSpan( - ITextSnapshot snapshot, - Span span) + if (endLine < snapshot.LineCount) { - var startLine = snapshot.GetLineNumberFromPosition(span.Start); - var endLine = snapshot.GetLineNumberFromPosition(span.End); + endLine++; + } - if (startLine > 0) - { - startLine--; - } + return LineSpan.FromBounds(startLine, endLine); + } - if (endLine < snapshot.LineCount) + // Adds a line span to the spans we've been collecting. If the line span overlaps or + // abuts a previous span then the two are merged. + private static void MergeLineSpans(List lineSpans, LineSpan nextLineSpan) + { + if (lineSpans.Count > 0) + { + var lastLineSpan = lineSpans.Last(); + + // We merge them if there's no more than one line between the two. Otherwise + // we'd show "..." between two spans where we could just show the actual code. + if (nextLineSpan.Start >= lastLineSpan.Start && nextLineSpan.Start <= (lastLineSpan.End + 1)) { - endLine++; + nextLineSpan = LineSpan.FromBounds(lastLineSpan.Start, nextLineSpan.End); + lineSpans.RemoveAt(lineSpans.Count - 1); } - - return LineSpan.FromBounds(startLine, endLine); } - // Adds a line span to the spans we've been collecting. If the line span overlaps or - // abuts a previous span then the two are merged. - private static void MergeLineSpans(List lineSpans, LineSpan nextLineSpan) - { - if (lineSpans.Count > 0) - { - var lastLineSpan = lineSpans.Last(); - - // We merge them if there's no more than one line between the two. Otherwise - // we'd show "..." between two spans where we could just show the actual code. - if (nextLineSpan.Start >= lastLineSpan.Start && nextLineSpan.Start <= (lastLineSpan.End + 1)) - { - nextLineSpan = LineSpan.FromBounds(lastLineSpan.Start, nextLineSpan.End); - lineSpans.RemoveAt(lineSpans.Count - 1); - } - } + lineSpans.Add(nextLineSpan); + } - lineSpans.Add(nextLineSpan); - } + private IHierarchicalDifferenceCollection ComputeEditDifferences(TextDocument oldDocument, TextDocument newDocument, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - private IHierarchicalDifferenceCollection ComputeEditDifferences(TextDocument oldDocument, TextDocument newDocument, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + // Get the text that's actually in the editor. + var oldText = oldDocument.GetTextSynchronously(cancellationToken); + var newText = newDocument.GetTextSynchronously(cancellationToken); - // Get the text that's actually in the editor. - var oldText = oldDocument.GetTextSynchronously(cancellationToken); - var newText = newDocument.GetTextSynchronously(cancellationToken); + // Defer to the editor to figure out what changes the client made. + var diffService = _differenceSelectorService.GetTextDifferencingService( + oldDocument.Project.Services.GetRequiredService().GetDefaultContentType()); - // Defer to the editor to figure out what changes the client made. - var diffService = _differenceSelectorService.GetTextDifferencingService( - oldDocument.Project.Services.GetRequiredService().GetDefaultContentType()); + diffService ??= _differenceSelectorService.DefaultTextDifferencingService; + return diffService.DiffStrings(oldText.ToString(), newText.ToString(), new StringDifferenceOptions() + { + DifferenceType = StringDifferenceTypes.Word | StringDifferenceTypes.Line, + }); + } - diffService ??= _differenceSelectorService.DefaultTextDifferencingService; - return diffService.DiffStrings(oldText.ToString(), newText.ToString(), new StringDifferenceOptions() - { - DifferenceType = StringDifferenceTypes.Word | StringDifferenceTypes.Line, - }); - } + private static NormalizedSpanCollection GetOriginalSpans(IHierarchicalDifferenceCollection diffResult, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + var lineSpans = new List(); - private static NormalizedSpanCollection GetOriginalSpans(IHierarchicalDifferenceCollection diffResult, CancellationToken cancellationToken) + foreach (var difference in diffResult) { cancellationToken.ThrowIfCancellationRequested(); - var lineSpans = new List(); + var mappedSpan = diffResult.LeftDecomposition.GetSpanInOriginal(difference.Left); + lineSpans.Add(mappedSpan); + } - foreach (var difference in diffResult) - { - cancellationToken.ThrowIfCancellationRequested(); - var mappedSpan = diffResult.LeftDecomposition.GetSpanInOriginal(difference.Left); - lineSpans.Add(mappedSpan); - } + return new NormalizedSpanCollection(lineSpans); + } - return new NormalizedSpanCollection(lineSpans); - } + private static NormalizedSpanCollection GetChangedSpans(IHierarchicalDifferenceCollection diffResult, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + var lineSpans = new List(); - private static NormalizedSpanCollection GetChangedSpans(IHierarchicalDifferenceCollection diffResult, CancellationToken cancellationToken) + foreach (var difference in diffResult) { cancellationToken.ThrowIfCancellationRequested(); - var lineSpans = new List(); - - foreach (var difference in diffResult) - { - cancellationToken.ThrowIfCancellationRequested(); - var mappedSpan = diffResult.RightDecomposition.GetSpanInOriginal(difference.Right); - lineSpans.Add(mappedSpan); - } - - return new NormalizedSpanCollection(lineSpans); + var mappedSpan = diffResult.RightDecomposition.GetSpanInOriginal(difference.Right); + lineSpans.Add(mappedSpan); } + + return new NormalizedSpanCollection(lineSpans); } } From 21ecb35e912b7daca3f7b305757520e54e238a01 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:25:00 -0800 Subject: [PATCH 049/151] file scoped --- .../Core/Shared/Preview/PreviewWorkspace.cs | 155 +++++++++--------- 1 file changed, 77 insertions(+), 78 deletions(-) diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs index e4347bd04aa40..31a630e5aac8b 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs @@ -10,109 +10,108 @@ using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.Editor.Shared.Preview +namespace Microsoft.CodeAnalysis.Editor.Shared.Preview; + +internal class PreviewWorkspace : Workspace { - internal class PreviewWorkspace : Workspace + public PreviewWorkspace() + : base(MefHostServices.DefaultHost, WorkspaceKind.Preview) { - public PreviewWorkspace() - : base(MefHostServices.DefaultHost, WorkspaceKind.Preview) - { - } + } - public PreviewWorkspace(HostServices hostServices) - : base(hostServices, WorkspaceKind.Preview) - { - } + public PreviewWorkspace(HostServices hostServices) + : base(hostServices, WorkspaceKind.Preview) + { + } - public PreviewWorkspace(Solution solution) - : base(solution.Workspace.Services.HostServices, WorkspaceKind.Preview) - { - var (oldSolution, newSolution) = this.SetCurrentSolutionEx(solution); + public PreviewWorkspace(Solution solution) + : base(solution.Workspace.Services.HostServices, WorkspaceKind.Preview) + { + var (oldSolution, newSolution) = this.SetCurrentSolutionEx(solution); - this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.SolutionChanged, oldSolution, newSolution); - } + this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.SolutionChanged, oldSolution, newSolution); + } #if false - public void EnableSolutionCrawler() - { - Services.GetRequiredService().Register(this); - } + public void EnableSolutionCrawler() + { + Services.GetRequiredService().Register(this); + } #endif - public override bool CanApplyChange(ApplyChangesKind feature) + public override bool CanApplyChange(ApplyChangesKind feature) + { + // one can manipulate preview workspace solution as mush as they want. + return true; + } + + // This method signature is the base method signature which should be used for a client of a workspace to + // tell the host to open it; in our case we want to open documents directly by passing the known buffer we created + // for it. + [Obsolete("Do not call the base OpenDocument method; instead call the overload that takes a container.", error: true)] + public new void OpenDocument(DocumentId documentId, bool activate = true) + { + } + + public void OpenDocument(DocumentId documentId, SourceTextContainer textContainer) + { + var document = this.CurrentSolution.GetTextDocument(documentId); + + // This could be null if we're previewing a source generated document; we can't wire those up yet + // TODO: implement this + if (document == null) { - // one can manipulate preview workspace solution as mush as they want. - return true; + return; } - // This method signature is the base method signature which should be used for a client of a workspace to - // tell the host to open it; in our case we want to open documents directly by passing the known buffer we created - // for it. - [Obsolete("Do not call the base OpenDocument method; instead call the overload that takes a container.", error: true)] - public new void OpenDocument(DocumentId documentId, bool activate = true) + if (document is AnalyzerConfigDocument) { + this.OnAnalyzerConfigDocumentOpened(documentId, textContainer); } - - public void OpenDocument(DocumentId documentId, SourceTextContainer textContainer) + else if (document is Document) { - var document = this.CurrentSolution.GetTextDocument(documentId); - - // This could be null if we're previewing a source generated document; we can't wire those up yet - // TODO: implement this - if (document == null) - { - return; - } - - if (document is AnalyzerConfigDocument) - { - this.OnAnalyzerConfigDocumentOpened(documentId, textContainer); - } - else if (document is Document) - { - this.OnDocumentOpened(documentId, textContainer); - } - else - { - this.OnAdditionalDocumentOpened(documentId, textContainer); - } + this.OnDocumentOpened(documentId, textContainer); } - - public override void CloseDocument(DocumentId documentId) + else { - var document = this.CurrentSolution.GetRequiredDocument(documentId); - var text = document.GetTextSynchronously(CancellationToken.None); - var version = document.GetTextVersionSynchronously(CancellationToken.None); - - this.OnDocumentClosed(documentId, TextLoader.From(TextAndVersion.Create(text, version))); + this.OnAdditionalDocumentOpened(documentId, textContainer); } + } - public override void CloseAdditionalDocument(DocumentId documentId) - { - var document = this.CurrentSolution.GetRequiredAdditionalDocument(documentId); - var text = document.GetTextSynchronously(CancellationToken.None); - var version = document.GetTextVersionSynchronously(CancellationToken.None); + public override void CloseDocument(DocumentId documentId) + { + var document = this.CurrentSolution.GetRequiredDocument(documentId); + var text = document.GetTextSynchronously(CancellationToken.None); + var version = document.GetTextVersionSynchronously(CancellationToken.None); - this.OnAdditionalDocumentClosed(documentId, TextLoader.From(TextAndVersion.Create(text, version))); - } + this.OnDocumentClosed(documentId, TextLoader.From(TextAndVersion.Create(text, version))); + } - public override void CloseAnalyzerConfigDocument(DocumentId documentId) - { - var document = this.CurrentSolution.GetRequiredAnalyzerConfigDocument(documentId); - var text = document.GetTextSynchronously(CancellationToken.None); - var version = document.GetTextVersionSynchronously(CancellationToken.None); + public override void CloseAdditionalDocument(DocumentId documentId) + { + var document = this.CurrentSolution.GetRequiredAdditionalDocument(documentId); + var text = document.GetTextSynchronously(CancellationToken.None); + var version = document.GetTextVersionSynchronously(CancellationToken.None); - this.OnAnalyzerConfigDocumentClosed(documentId, TextLoader.From(TextAndVersion.Create(text, version))); - } + this.OnAdditionalDocumentClosed(documentId, TextLoader.From(TextAndVersion.Create(text, version))); + } - protected override void Dispose(bool finalize) - { - base.Dispose(finalize); + public override void CloseAnalyzerConfigDocument(DocumentId documentId) + { + var document = this.CurrentSolution.GetRequiredAnalyzerConfigDocument(documentId); + var text = document.GetTextSynchronously(CancellationToken.None); + var version = document.GetTextVersionSynchronously(CancellationToken.None); + + this.OnAnalyzerConfigDocumentClosed(documentId, TextLoader.From(TextAndVersion.Create(text, version))); + } + + protected override void Dispose(bool finalize) + { + base.Dispose(finalize); #if false - Services.GetRequiredService().Unregister(this); + Services.GetRequiredService().Unregister(this); #endif - ClearSolution(); - } + ClearSolution(); } } From 15e39bc29c03d67896ec6da7339bdbeba4cb4cdf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:25:14 -0800 Subject: [PATCH 050/151] file scoped --- ...stSolutionCrawlerWorkspaceEventListener.cs | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs index 044b1fa41e581..a540acd3b196f 100644 --- a/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs @@ -8,33 +8,32 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -namespace Microsoft.CodeAnalysis.SolutionCrawler +namespace Microsoft.CodeAnalysis.SolutionCrawler; + +[ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class HostSolutionCrawlerWorkspaceEventListener(IGlobalOptionService globalOptions) : IEventListener, IEventListenerStoppable { - [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - internal sealed class HostSolutionCrawlerWorkspaceEventListener(IGlobalOptionService globalOptions) : IEventListener, IEventListenerStoppable - { - private readonly IGlobalOptionService _globalOptions = globalOptions; + private readonly IGlobalOptionService _globalOptions = globalOptions; - public void StartListening(Workspace workspace, object? serviceOpt) - { + public void StartListening(Workspace workspace, object? serviceOpt) + { #if false - if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - workspace.Services.GetRequiredService().Register(workspace); - } -#endif + if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) + { + workspace.Services.GetRequiredService().Register(workspace); } +#endif + } - public void StopListening(Workspace workspace) - { + public void StopListening(Workspace workspace) + { #if false - // we do this so that we can stop solution crawler faster and fire some telemetry. - // this is to reduce a case where we keep going even when VS is shutting down since we don't know about that - var registration = workspace.Services.GetRequiredService(); - registration.Unregister(workspace, blockingShutdown: true); + // we do this so that we can stop solution crawler faster and fire some telemetry. + // this is to reduce a case where we keep going even when VS is shutting down since we don't know about that + var registration = workspace.Services.GetRequiredService(); + registration.Unregister(workspace, blockingShutdown: true); #endif - } } } From 8116f2f201182cf0fe2af0a4bba449b19e4cf9c9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:25:31 -0800 Subject: [PATCH 051/151] file scoped --- ...acySolutionEventsWorkspaceEventListener.cs | 159 +++++++++--------- 1 file changed, 79 insertions(+), 80 deletions(-) diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs index 4e8440906c673..157e61acda86d 100644 --- a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -17,104 +17,103 @@ using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.LegacySolutionEvents +namespace Microsoft.CodeAnalysis.LegacySolutionEvents; + +[ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] +internal sealed partial class HostLegacySolutionEventsWorkspaceEventListener : IEventListener { - [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] - internal sealed partial class HostLegacySolutionEventsWorkspaceEventListener : IEventListener + private readonly IGlobalOptionService _globalOptions; + private readonly IThreadingContext _threadingContext; + private readonly AsyncBatchingWorkQueue _eventQueue; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public HostLegacySolutionEventsWorkspaceEventListener( + IGlobalOptionService globalOptions, + IThreadingContext threadingContext, + IAsynchronousOperationListenerProvider listenerProvider) { - private readonly IGlobalOptionService _globalOptions; - private readonly IThreadingContext _threadingContext; - private readonly AsyncBatchingWorkQueue _eventQueue; + _globalOptions = globalOptions; + _threadingContext = threadingContext; + _eventQueue = new AsyncBatchingWorkQueue( + DelayTimeSpan.Short, + ProcessWorkspaceChangeEventsAsync, + listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerUnitTesting), + _threadingContext.DisposalToken); + } - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public HostLegacySolutionEventsWorkspaceEventListener( - IGlobalOptionService globalOptions, - IThreadingContext threadingContext, - IAsynchronousOperationListenerProvider listenerProvider) + public void StartListening(Workspace workspace, object? serviceOpt) + { +#if false + if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { - _globalOptions = globalOptions; - _threadingContext = threadingContext; - _eventQueue = new AsyncBatchingWorkQueue( - DelayTimeSpan.Short, - ProcessWorkspaceChangeEventsAsync, - listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerUnitTesting), - _threadingContext.DisposalToken); + workspace.WorkspaceChanged += OnWorkspaceChanged; + _threadingContext.DisposalToken.Register(() => + { + workspace.WorkspaceChanged -= OnWorkspaceChanged; + }); } +#endif + } - public void StartListening(Workspace workspace, object? serviceOpt) + private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) + { + // Legacy workspace events exist solely to let unit testing continue to work using their own fork of solution + // crawler. As such, they only need events for the project types they care about. Specifically, that is only + // for VB and C#. This is relevant as well as we don't sync any other project types to OOP. So sending + // notifications about other projects that don't even exist on the other side isn't helpful. + + var projectId = e.ProjectId ?? e.DocumentId?.ProjectId; + if (projectId != null) { -#if false - if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - workspace.WorkspaceChanged += OnWorkspaceChanged; - _threadingContext.DisposalToken.Register(() => - { - workspace.WorkspaceChanged -= OnWorkspaceChanged; - }); - } -#endif + var project = e.OldSolution.GetProject(projectId) ?? e.NewSolution.GetProject(projectId); + if (project != null && !RemoteSupportedLanguages.IsSupported(project.Language)) + return; } - private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) - { - // Legacy workspace events exist solely to let unit testing continue to work using their own fork of solution - // crawler. As such, they only need events for the project types they care about. Specifically, that is only - // for VB and C#. This is relevant as well as we don't sync any other project types to OOP. So sending - // notifications about other projects that don't even exist on the other side isn't helpful. + _eventQueue.AddWork(e); + } - var projectId = e.ProjectId ?? e.DocumentId?.ProjectId; - if (projectId != null) - { - var project = e.OldSolution.GetProject(projectId) ?? e.NewSolution.GetProject(projectId); - if (project != null && !RemoteSupportedLanguages.IsSupported(project.Language)) - return; - } + private async ValueTask ProcessWorkspaceChangeEventsAsync(ImmutableSegmentedList events, CancellationToken cancellationToken) + { + if (events.IsEmpty) + return; - _eventQueue.AddWork(e); - } + var workspace = events[0].OldSolution.Workspace; + Contract.ThrowIfTrue(events.Any(e => e.OldSolution.Workspace != workspace || e.NewSolution.Workspace != workspace)); - private async ValueTask ProcessWorkspaceChangeEventsAsync(ImmutableSegmentedList events, CancellationToken cancellationToken) + var client = await RemoteHostClient.TryGetClientAsync(workspace, cancellationToken).ConfigureAwait(false); + + if (client is null) { - if (events.IsEmpty) + var aggregationService = workspace.Services.GetRequiredService(); + var shouldReport = aggregationService.ShouldReportChanges(workspace.Services.SolutionServices); + if (!shouldReport) return; - var workspace = events[0].OldSolution.Workspace; - Contract.ThrowIfTrue(events.Any(e => e.OldSolution.Workspace != workspace || e.NewSolution.Workspace != workspace)); - - var client = await RemoteHostClient.TryGetClientAsync(workspace, cancellationToken).ConfigureAwait(false); - - if (client is null) - { - var aggregationService = workspace.Services.GetRequiredService(); - var shouldReport = aggregationService.ShouldReportChanges(workspace.Services.SolutionServices); - if (!shouldReport) - return; + foreach (var args in events) + await aggregationService.OnWorkspaceChangedAsync(args, cancellationToken).ConfigureAwait(false); + } + else + { + // Notifying OOP of workspace events can be expensive (there may be a lot of them, and they involve + // syncing over entire solution snapshots). As such, do not bother to do this if the remote side says + // that it's not interested in the events. This will happen, for example, when the unittesting + // Test-Explorer window has not been shown yet, and so the unit testing system will not have registered + // an incremental analyzer with us. + var shouldReport = await client.TryInvokeAsync( + (service, cancellationToken) => service.ShouldReportChangesAsync(cancellationToken), + cancellationToken).ConfigureAwait(false); + if (!shouldReport.HasValue || !shouldReport.Value) + return; - foreach (var args in events) - await aggregationService.OnWorkspaceChangedAsync(args, cancellationToken).ConfigureAwait(false); - } - else + foreach (var args in events) { - // Notifying OOP of workspace events can be expensive (there may be a lot of them, and they involve - // syncing over entire solution snapshots). As such, do not bother to do this if the remote side says - // that it's not interested in the events. This will happen, for example, when the unittesting - // Test-Explorer window has not been shown yet, and so the unit testing system will not have registered - // an incremental analyzer with us. - var shouldReport = await client.TryInvokeAsync( - (service, cancellationToken) => service.ShouldReportChangesAsync(cancellationToken), + await client.TryInvokeAsync( + args.OldSolution, args.NewSolution, + (service, oldSolutionChecksum, newSolutionChecksum, cancellationToken) => + service.OnWorkspaceChangedAsync(oldSolutionChecksum, newSolutionChecksum, args.Kind, args.ProjectId, args.DocumentId, cancellationToken), cancellationToken).ConfigureAwait(false); - if (!shouldReport.HasValue || !shouldReport.Value) - return; - - foreach (var args in events) - { - await client.TryInvokeAsync( - args.OldSolution, args.NewSolution, - (service, oldSolutionChecksum, newSolutionChecksum, cancellationToken) => - service.OnWorkspaceChangedAsync(oldSolutionChecksum, newSolutionChecksum, args.Kind, args.ProjectId, args.DocumentId, cancellationToken), - cancellationToken).ConfigureAwait(false); - } } } } From 3d989c07f55f8d6a73cf572d7c3bf8fd73fac93d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:25:56 -0800 Subject: [PATCH 052/151] file scoped --- .../AbstractDocumentDifferenceService.cs | 255 +++++++++--------- 1 file changed, 127 insertions(+), 128 deletions(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs index b6173b7629859..fc434d7a511e2 100644 --- a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs @@ -11,170 +11,169 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.SolutionCrawler +namespace Microsoft.CodeAnalysis.SolutionCrawler; + +internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceService { - internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceService + public async Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { - public async Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) + try { - try + var syntaxFactsService = newDocument.Project.Services.GetService(); + if (syntaxFactsService == null) { - var syntaxFactsService = newDocument.Project.Services.GetService(); - if (syntaxFactsService == null) - { - // somehow, we can't get the service. without it, there is nothing we can do. - return null; - } - // this is based on the implementation detail where opened documents use strong references - // to tree and text rather than recoverable versions. - if (!oldDocument.TryGetText(out var oldText) || - !newDocument.TryGetText(out var newText)) - { - // no cheap way to determine top level changes. assumes top level has changed - return null; - } - // quick check whether two tree versions are same - if (oldDocument.TryGetSyntaxVersion(out var oldVersion) && - newDocument.TryGetSyntaxVersion(out var newVersion) && - oldVersion.Equals(newVersion)) - { - // nothing has changed. don't do anything. - // this could happen if a document is opened/closed without any buffer change - return null; - } + // somehow, we can't get the service. without it, there is nothing we can do. + return null; + } + // this is based on the implementation detail where opened documents use strong references + // to tree and text rather than recoverable versions. + if (!oldDocument.TryGetText(out var oldText) || + !newDocument.TryGetText(out var newText)) + { + // no cheap way to determine top level changes. assumes top level has changed + return null; + } + // quick check whether two tree versions are same + if (oldDocument.TryGetSyntaxVersion(out var oldVersion) && + newDocument.TryGetSyntaxVersion(out var newVersion) && + oldVersion.Equals(newVersion)) + { + // nothing has changed. don't do anything. + // this could happen if a document is opened/closed without any buffer change + return null; + } - var range = newText.GetEncompassingTextChangeRange(oldText); - if (range == default) - { - // nothing has changed. don't do anything - return null; - } + var range = newText.GetEncompassingTextChangeRange(oldText); + if (range == default) + { + // nothing has changed. don't do anything + return null; + } - var incrementalParsingCandidate = range.NewLength != newText.Length; - // see whether we can get it without explicit parsing - if (!oldDocument.TryGetSyntaxRoot(out var oldRoot) || - !newDocument.TryGetSyntaxRoot(out var newRoot)) + var incrementalParsingCandidate = range.NewLength != newText.Length; + // see whether we can get it without explicit parsing + if (!oldDocument.TryGetSyntaxRoot(out var oldRoot) || + !newDocument.TryGetSyntaxRoot(out var newRoot)) + { + if (!incrementalParsingCandidate) { - if (!incrementalParsingCandidate) - { - // no cheap way to determine top level changes. assumes top level has changed - return null; - } - - // explicitly parse them - oldRoot = await oldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - - Contract.ThrowIfNull(oldRoot); - Contract.ThrowIfNull(newRoot); + // no cheap way to determine top level changes. assumes top level has changed + return null; } - // at this point, we must have these version already calculated - if (!oldDocument.TryGetTopLevelChangeTextVersion(out var oldTopLevelChangeVersion) || - !newDocument.TryGetTopLevelChangeTextVersion(out var newTopLevelChangeVersion)) - { - throw ExceptionUtilities.Unreachable(); - } + // explicitly parse them + oldRoot = await oldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - // quicker common case - if (incrementalParsingCandidate) - { - if (oldTopLevelChangeVersion.Equals(newTopLevelChangeVersion)) - { - return GetChangedMember(syntaxFactsService, oldRoot, newRoot, range); - } + Contract.ThrowIfNull(oldRoot); + Contract.ThrowIfNull(newRoot); + } - return GetBestGuessChangedMember(syntaxFactsService, oldRoot, newRoot, range); - } + // at this point, we must have these version already calculated + if (!oldDocument.TryGetTopLevelChangeTextVersion(out var oldTopLevelChangeVersion) || + !newDocument.TryGetTopLevelChangeTextVersion(out var newTopLevelChangeVersion)) + { + throw ExceptionUtilities.Unreachable(); + } + // quicker common case + if (incrementalParsingCandidate) + { if (oldTopLevelChangeVersion.Equals(newTopLevelChangeVersion)) { - return null; + return GetChangedMember(syntaxFactsService, oldRoot, newRoot, range); } - return null; + return GetBestGuessChangedMember(syntaxFactsService, oldRoot, newRoot, range); } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - } - private static SyntaxNode? GetChangedMember( - ISyntaxFactsService syntaxFactsService, SyntaxNode oldRoot, SyntaxNode newRoot, TextChangeRange range) - { - // if either old or new tree contains skipped text, re-analyze whole document - if (oldRoot.ContainsSkippedText || newRoot.ContainsSkippedText) + if (oldTopLevelChangeVersion.Equals(newTopLevelChangeVersion)) { return null; } - var oldMember = syntaxFactsService.GetContainingMemberDeclaration(oldRoot, range.Span.Start); - var newMember = syntaxFactsService.GetContainingMemberDeclaration(newRoot, range.Span.Start); + return null; + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) + { + throw ExceptionUtilities.Unreachable(); + } + } - // reached the top (compilation unit) - if (oldMember == null || newMember == null) - { - return null; - } + private static SyntaxNode? GetChangedMember( + ISyntaxFactsService syntaxFactsService, SyntaxNode oldRoot, SyntaxNode newRoot, TextChangeRange range) + { + // if either old or new tree contains skipped text, re-analyze whole document + if (oldRoot.ContainsSkippedText || newRoot.ContainsSkippedText) + { + return null; + } - // member doesn't contain the change - if (!syntaxFactsService.ContainsInMemberBody(oldMember, range.Span)) - { - return null; - } + var oldMember = syntaxFactsService.GetContainingMemberDeclaration(oldRoot, range.Span.Start); + var newMember = syntaxFactsService.GetContainingMemberDeclaration(newRoot, range.Span.Start); - // member signature has changed - if (!oldMember.IsEquivalentTo(newMember, topLevel: true)) - { - return null; - } + // reached the top (compilation unit) + if (oldMember == null || newMember == null) + { + return null; + } - // looks like inside of the body has changed - return newMember; + // member doesn't contain the change + if (!syntaxFactsService.ContainsInMemberBody(oldMember, range.Span)) + { + return null; } - private static SyntaxNode? GetBestGuessChangedMember( - ISyntaxFactsService syntaxFactsService, SyntaxNode oldRoot, SyntaxNode newRoot, TextChangeRange range) + // member signature has changed + if (!oldMember.IsEquivalentTo(newMember, topLevel: true)) { - // if either old or new tree contains skipped text, re-analyze whole document - if (oldRoot.ContainsSkippedText || newRoot.ContainsSkippedText) - { - return null; - } + return null; + } - // there was top level changes, so we can't use equivalent to see whether two members are same. - // so, we use some simple text based heuristic to find a member that has changed. - // - // if we have a differ that do diff on member level or a way to track member between incremental parsing, then - // that would be preferable. but currently we don't have such thing. + // looks like inside of the body has changed + return newMember; + } - // get top level elements at the position where change has happened - var oldMember = syntaxFactsService.GetContainingMemberDeclaration(oldRoot, range.Span.Start); - var newMember = syntaxFactsService.GetContainingMemberDeclaration(newRoot, range.Span.Start); + private static SyntaxNode? GetBestGuessChangedMember( + ISyntaxFactsService syntaxFactsService, SyntaxNode oldRoot, SyntaxNode newRoot, TextChangeRange range) + { + // if either old or new tree contains skipped text, re-analyze whole document + if (oldRoot.ContainsSkippedText || newRoot.ContainsSkippedText) + { + return null; + } - // reached the top (compilation unit) - if (oldMember == null || newMember == null) - { - return null; - } + // there was top level changes, so we can't use equivalent to see whether two members are same. + // so, we use some simple text based heuristic to find a member that has changed. + // + // if we have a differ that do diff on member level or a way to track member between incremental parsing, then + // that would be preferable. but currently we don't have such thing. - // if old member was empty, just use new member - if (oldMember.Span.IsEmpty) - { - return newMember; - } + // get top level elements at the position where change has happened + var oldMember = syntaxFactsService.GetContainingMemberDeclaration(oldRoot, range.Span.Start); + var newMember = syntaxFactsService.GetContainingMemberDeclaration(newRoot, range.Span.Start); - // looks like change doesn't belong to existing member - if (!oldMember.Span.Contains(range.Span)) - { - return null; - } + // reached the top (compilation unit) + if (oldMember == null || newMember == null) + { + return null; + } - // change happened inside of the old member, check whether new member seems just delta of that change - var lengthDelta = range.NewLength - range.Span.Length; + // if old member was empty, just use new member + if (oldMember.Span.IsEmpty) + { + return newMember; + } - return (oldMember.Span.Length + lengthDelta) == newMember.Span.Length ? newMember : null; + // looks like change doesn't belong to existing member + if (!oldMember.Span.Contains(range.Span)) + { + return null; } + + // change happened inside of the old member, check whether new member seems just delta of that change + var lengthDelta = range.NewLength - range.Span.Length; + + return (oldMember.Span.Length + lengthDelta) == newMember.Span.Length ? newMember : null; } } From fc3d1d3af11a6abfc14544d65832082ed9eb2b8c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:26:12 -0800 Subject: [PATCH 053/151] file scoped --- .../IDocumentDifferenceService.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs b/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs index 440a3c4e435af..9f1e0a9e45c5b 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs @@ -6,21 +6,20 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; -namespace Microsoft.CodeAnalysis.SolutionCrawler -{ +namespace Microsoft.CodeAnalysis.SolutionCrawler; + #if false - internal class DocumentDifferenceResult(InvocationReasons changeType, SyntaxNode? changedMember = null) - { - public InvocationReasons ChangeType { get; } = changeType; - public SyntaxNode? ChangedMember { get; } = changedMember; - } +internal class DocumentDifferenceResult(InvocationReasons changeType, SyntaxNode? changedMember = null) +{ + public InvocationReasons ChangeType { get; } = changeType; + public SyntaxNode? ChangedMember { get; } = changedMember; +} #endif - internal interface IDocumentDifferenceService : ILanguageService - { +internal interface IDocumentDifferenceService : ILanguageService +{ #if false - Task GetDifferenceAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); + Task GetDifferenceAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); #endif - Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); - } + Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); } From 3fff34c71bccb17c16a140debf8c08aa549f0d61 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:27:23 -0800 Subject: [PATCH 054/151] file scoped --- src/VisualStudio/Core/Def/RoslynPackage.cs | 489 ++++++++++----------- 1 file changed, 244 insertions(+), 245 deletions(-) diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 2c4904ec79c73..71a105241e509 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -44,341 +44,340 @@ using Roslyn.Utilities; using Task = System.Threading.Tasks.Task; -namespace Microsoft.VisualStudio.LanguageServices.Setup +namespace Microsoft.VisualStudio.LanguageServices.Setup; + +[Guid(Guids.RoslynPackageIdString)] + +// The option page configuration is duplicated in PackageRegistration.pkgdef +[ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] +[ProvideToolWindow(typeof(StackTraceExplorerToolWindow))] +internal sealed class RoslynPackage : AbstractPackage { - [Guid(Guids.RoslynPackageIdString)] + // The randomly-generated key name is used for serializing the Background Analysis Scope preference to the .SUO + // file. It doesn't have any semantic meaning, but is intended to not conflict with any other extension that + // might be saving an "AnalysisScope" named stream to the same file. + // note: must be <= 31 characters long + private const string BackgroundAnalysisScopeOptionKey = "AnalysisScope-DCE33A29A768"; + private const byte BackgroundAnalysisScopeOptionVersion = 1; - // The option page configuration is duplicated in PackageRegistration.pkgdef - [ProvideToolWindow(typeof(ValueTracking.ValueTrackingToolWindow))] - [ProvideToolWindow(typeof(StackTraceExplorerToolWindow))] - internal sealed class RoslynPackage : AbstractPackage - { - // The randomly-generated key name is used for serializing the Background Analysis Scope preference to the .SUO - // file. It doesn't have any semantic meaning, but is intended to not conflict with any other extension that - // might be saving an "AnalysisScope" named stream to the same file. - // note: must be <= 31 characters long - private const string BackgroundAnalysisScopeOptionKey = "AnalysisScope-DCE33A29A768"; - private const byte BackgroundAnalysisScopeOptionVersion = 1; + private static RoslynPackage? _lazyInstance; - private static RoslynPackage? _lazyInstance; + private RuleSetEventHandler? _ruleSetEventHandler; + private ColorSchemeApplier? _colorSchemeApplier; + private IDisposable? _solutionEventMonitor; - private RuleSetEventHandler? _ruleSetEventHandler; - private ColorSchemeApplier? _colorSchemeApplier; - private IDisposable? _solutionEventMonitor; + private BackgroundAnalysisScope? _analysisScope; - private BackgroundAnalysisScope? _analysisScope; + public RoslynPackage() + { + // We need to register an option in order for OnLoadOptions/OnSaveOptions to be called + AddOptionKey(BackgroundAnalysisScopeOptionKey); + } - public RoslynPackage() + public BackgroundAnalysisScope? AnalysisScope + { + get { - // We need to register an option in order for OnLoadOptions/OnSaveOptions to be called - AddOptionKey(BackgroundAnalysisScopeOptionKey); + return _analysisScope; } - public BackgroundAnalysisScope? AnalysisScope + set { - get - { - return _analysisScope; - } - - set - { - if (_analysisScope == value) - return; + if (_analysisScope == value) + return; - _analysisScope = value; - AnalysisScopeChanged?.Invoke(this, EventArgs.Empty); - } + _analysisScope = value; + AnalysisScopeChanged?.Invoke(this, EventArgs.Empty); } + } - public event EventHandler? AnalysisScopeChanged; + public event EventHandler? AnalysisScopeChanged; - internal static async ValueTask GetOrLoadAsync(IThreadingContext threadingContext, IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken) + internal static async ValueTask GetOrLoadAsync(IThreadingContext threadingContext, IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken) + { + if (_lazyInstance is null) { - if (_lazyInstance is null) - { - await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var shell = (IVsShell7?)await serviceProvider.GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); - Assumes.Present(shell); - await shell.LoadPackageAsync(typeof(RoslynPackage).GUID); + var shell = (IVsShell7?)await serviceProvider.GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); + Assumes.Present(shell); + await shell.LoadPackageAsync(typeof(RoslynPackage).GUID); - if (ErrorHandler.Succeeded(((IVsShell)shell).IsPackageLoaded(typeof(RoslynPackage).GUID, out var package))) - { - _lazyInstance = (RoslynPackage)package; - } + if (ErrorHandler.Succeeded(((IVsShell)shell).IsPackageLoaded(typeof(RoslynPackage).GUID, out var package))) + { + _lazyInstance = (RoslynPackage)package; } - - return _lazyInstance; } - protected override void OnLoadOptions(string key, Stream stream) + return _lazyInstance; + } + + protected override void OnLoadOptions(string key, Stream stream) + { + if (key == BackgroundAnalysisScopeOptionKey) { - if (key == BackgroundAnalysisScopeOptionKey) + if (stream.ReadByte() == BackgroundAnalysisScopeOptionVersion) { - if (stream.ReadByte() == BackgroundAnalysisScopeOptionVersion) - { - var hasValue = stream.ReadByte() == 1; - AnalysisScope = hasValue ? (BackgroundAnalysisScope)stream.ReadByte() : null; - } - else - { - AnalysisScope = null; - } + var hasValue = stream.ReadByte() == 1; + AnalysisScope = hasValue ? (BackgroundAnalysisScope)stream.ReadByte() : null; } - - base.OnLoadOptions(key, stream); - } - - protected override void OnSaveOptions(string key, Stream stream) - { - if (key == BackgroundAnalysisScopeOptionKey) + else { - stream.WriteByte(BackgroundAnalysisScopeOptionVersion); - stream.WriteByte(AnalysisScope.HasValue ? (byte)1 : (byte)0); - stream.WriteByte((byte)AnalysisScope.GetValueOrDefault()); + AnalysisScope = null; } - - base.OnSaveOptions(key, stream); } - protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + base.OnLoadOptions(key, stream); + } + + protected override void OnSaveOptions(string key, Stream stream) + { + if (key == BackgroundAnalysisScopeOptionKey) { - await base.InitializeAsync(cancellationToken, progress).ConfigureAwait(true); + stream.WriteByte(BackgroundAnalysisScopeOptionVersion); + stream.WriteByte(AnalysisScope.HasValue ? (byte)1 : (byte)0); + stream.WriteByte((byte)AnalysisScope.GetValueOrDefault()); + } - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + base.OnSaveOptions(key, stream); + } - cancellationToken.ThrowIfCancellationRequested(); + protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + { + await base.InitializeAsync(cancellationToken, progress).ConfigureAwait(true); - // Ensure the options persisters are loaded since we have to fetch options from the shell - LoadOptionPersistersAsync(this.ComponentModel, cancellationToken).Forget(); + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - await InitializeColorsAsync(cancellationToken).ConfigureAwait(true); + cancellationToken.ThrowIfCancellationRequested(); - // load some services that have to be loaded in UI thread - LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(cancellationToken).Forget(); + // Ensure the options persisters are loaded since we have to fetch options from the shell + LoadOptionPersistersAsync(this.ComponentModel, cancellationToken).Forget(); - // We are at the VS layer, so we know we must be able to get the IGlobalOperationNotificationService here. - var globalNotificationService = this.ComponentModel.GetService(); - Assumes.Present(globalNotificationService); + await InitializeColorsAsync(cancellationToken).ConfigureAwait(true); - _solutionEventMonitor = new SolutionEventMonitor(globalNotificationService); - TrackBulkFileOperations(globalNotificationService); + // load some services that have to be loaded in UI thread + LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(cancellationToken).Forget(); - var settingsEditorFactory = this.ComponentModel.GetService(); - RegisterEditorFactory(settingsEditorFactory); + // We are at the VS layer, so we know we must be able to get the IGlobalOperationNotificationService here. + var globalNotificationService = this.ComponentModel.GetService(); + Assumes.Present(globalNotificationService); - // Misc workspace has to be up and running by the time our package is usable so that it can track running - // doc events and appropriately map files to/from it and other relevant workspaces (like the - // metadata-as-source workspace). - await this.ComponentModel.GetService().InitializeAsync().ConfigureAwait(false); + _solutionEventMonitor = new SolutionEventMonitor(globalNotificationService); + TrackBulkFileOperations(globalNotificationService); - // Proffer in-process service broker services - var serviceBrokerContainer = await this.GetServiceAsync(this.JoinableTaskFactory).ConfigureAwait(false); + var settingsEditorFactory = this.ComponentModel.GetService(); + RegisterEditorFactory(settingsEditorFactory); - serviceBrokerContainer.Proffer( - WorkspaceProjectFactoryServiceDescriptor.ServiceDescriptor, - (_, _, _, _) => ValueTaskFactory.FromResult(new WorkspaceProjectFactoryService(this.ComponentModel.GetService()))); - } + // Misc workspace has to be up and running by the time our package is usable so that it can track running + // doc events and appropriately map files to/from it and other relevant workspaces (like the + // metadata-as-source workspace). + await this.ComponentModel.GetService().InitializeAsync().ConfigureAwait(false); - private async Task LoadOptionPersistersAsync(IComponentModel componentModel, CancellationToken cancellationToken) - { - var listenerProvider = componentModel.GetService(); - using var token = listenerProvider.GetListener(FeatureAttribute.Workspace).BeginAsyncOperation(nameof(LoadOptionPersistersAsync)); + // Proffer in-process service broker services + var serviceBrokerContainer = await this.GetServiceAsync(this.JoinableTaskFactory).ConfigureAwait(false); - // Switch to a background thread to ensure assembly loads don't show up as UI delays attributed to - // InitializeAsync. - await TaskScheduler.Default; + serviceBrokerContainer.Proffer( + WorkspaceProjectFactoryServiceDescriptor.ServiceDescriptor, + (_, _, _, _) => ValueTaskFactory.FromResult(new WorkspaceProjectFactoryService(this.ComponentModel.GetService()))); + } - var persisterProviders = componentModel.GetExtensions().ToImmutableArray(); + private async Task LoadOptionPersistersAsync(IComponentModel componentModel, CancellationToken cancellationToken) + { + var listenerProvider = componentModel.GetService(); + using var token = listenerProvider.GetListener(FeatureAttribute.Workspace).BeginAsyncOperation(nameof(LoadOptionPersistersAsync)); - foreach (var provider in persisterProviders) - { - var persister = await provider.GetOrCreatePersisterAsync(cancellationToken).ConfigureAwait(true); + // Switch to a background thread to ensure assembly loads don't show up as UI delays attributed to + // InitializeAsync. + await TaskScheduler.Default; - // Initialize the PackageSettingsPersister to allow it to listen to analysis scope changed - // events from this package. - if (persister is PackageSettingsPersister packageSettingsPersister) - packageSettingsPersister.Initialize(this); - } - } + var persisterProviders = componentModel.GetExtensions().ToImmutableArray(); - private async Task InitializeColorsAsync(CancellationToken cancellationToken) + foreach (var provider in persisterProviders) { - await TaskScheduler.Default; - _colorSchemeApplier = ComponentModel.GetService(); - await _colorSchemeApplier.InitializeAsync(cancellationToken).ConfigureAwait(false); + var persister = await provider.GetOrCreatePersisterAsync(cancellationToken).ConfigureAwait(true); + + // Initialize the PackageSettingsPersister to allow it to listen to analysis scope changed + // events from this package. + if (persister is PackageSettingsPersister packageSettingsPersister) + packageSettingsPersister.Initialize(this); } + } - protected override async Task LoadComponentsAsync(CancellationToken cancellationToken) - { - await TaskScheduler.Default; + private async Task InitializeColorsAsync(CancellationToken cancellationToken) + { + await TaskScheduler.Default; + _colorSchemeApplier = ComponentModel.GetService(); + await _colorSchemeApplier.InitializeAsync(cancellationToken).ConfigureAwait(false); + } + + protected override async Task LoadComponentsAsync(CancellationToken cancellationToken) + { + await TaskScheduler.Default; - await GetServiceAsync(typeof(SVsTaskStatusCenterService)).ConfigureAwait(false); - await GetServiceAsync(typeof(SVsErrorList)).ConfigureAwait(false); - await GetServiceAsync(typeof(SVsSolution)).ConfigureAwait(false); - await GetServiceAsync(typeof(SVsShell)).ConfigureAwait(false); - await GetServiceAsync(typeof(SVsRunningDocumentTable)).ConfigureAwait(false); - await GetServiceAsync(typeof(SVsTextManager)).ConfigureAwait(false); + await GetServiceAsync(typeof(SVsTaskStatusCenterService)).ConfigureAwait(false); + await GetServiceAsync(typeof(SVsErrorList)).ConfigureAwait(false); + await GetServiceAsync(typeof(SVsSolution)).ConfigureAwait(false); + await GetServiceAsync(typeof(SVsShell)).ConfigureAwait(false); + await GetServiceAsync(typeof(SVsRunningDocumentTable)).ConfigureAwait(false); + await GetServiceAsync(typeof(SVsTextManager)).ConfigureAwait(false); - // we need to load it as early as possible since we can have errors from - // package from each language very early + // we need to load it as early as possible since we can have errors from + // package from each language very early #if false - await this.ComponentModel.GetService().InitializeAsync().ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync().ConfigureAwait(false); #endif - await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); - await LoadAnalyzerNodeComponentsAsync(cancellationToken).ConfigureAwait(false); + await LoadAnalyzerNodeComponentsAsync(cancellationToken).ConfigureAwait(false); - LoadComponentsBackgroundAsync(cancellationToken).ReportNonFatalErrorUnlessCancelledAsync(cancellationToken).Forget(); - } + LoadComponentsBackgroundAsync(cancellationToken).ReportNonFatalErrorUnlessCancelledAsync(cancellationToken).Forget(); + } - // Overrides for VSSDK003 fix - // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md - public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) + // Overrides for VSSDK003 fix + // See https://github.com/Microsoft/VSSDK-Analyzers/blob/main/doc/VSSDK003.md + public override IVsAsyncToolWindowFactory GetAsyncToolWindowFactory(Guid toolWindowType) + { + if (toolWindowType == typeof(ValueTracking.ValueTrackingToolWindow).GUID) { - if (toolWindowType == typeof(ValueTracking.ValueTrackingToolWindow).GUID) - { - return this; - } - - if (toolWindowType == typeof(StackTraceExplorerToolWindow).GUID) - { - return this; - } - - return base.GetAsyncToolWindowFactory(toolWindowType); + return this; } - protected override string GetToolWindowTitle(Type toolWindowType, int id) - => base.GetToolWindowTitle(toolWindowType, id); + if (toolWindowType == typeof(StackTraceExplorerToolWindow).GUID) + { + return this; + } - protected override Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) - => Task.FromResult((object?)null); + return base.GetAsyncToolWindowFactory(toolWindowType); + } - private async Task LoadComponentsBackgroundAsync(CancellationToken cancellationToken) - { - await TaskScheduler.Default; + protected override string GetToolWindowTitle(Type toolWindowType, int id) + => base.GetToolWindowTitle(toolWindowType, id); - await LoadStackTraceExplorerMenusAsync(cancellationToken).ConfigureAwait(true); + protected override Task InitializeToolWindowAsync(Type toolWindowType, int id, CancellationToken cancellationToken) + => Task.FromResult((object?)null); - // Initialize keybinding reset detector - await ComponentModel.DefaultExportProvider.GetExportedValue().InitializeAsync().ConfigureAwait(true); - } + private async Task LoadComponentsBackgroundAsync(CancellationToken cancellationToken) + { + await TaskScheduler.Default; - private async Task LoadStackTraceExplorerMenusAsync(CancellationToken cancellationToken) - { - // Obtain services and QueryInterface from the main thread - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + await LoadStackTraceExplorerMenusAsync(cancellationToken).ConfigureAwait(true); - var menuCommandService = (OleMenuCommandService)await GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true); - StackTraceExplorerCommandHandler.Initialize(menuCommandService, this); - } + // Initialize keybinding reset detector + await ComponentModel.DefaultExportProvider.GetExportedValue().InitializeAsync().ConfigureAwait(true); + } - protected override void Dispose(bool disposing) - { - UnregisterAnalyzerTracker(); - UnregisterRuleSetEventHandler(); + private async Task LoadStackTraceExplorerMenusAsync(CancellationToken cancellationToken) + { + // Obtain services and QueryInterface from the main thread + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - ReportSessionWideTelemetry(); + var menuCommandService = (OleMenuCommandService)await GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(true); + StackTraceExplorerCommandHandler.Initialize(menuCommandService, this); + } - if (_solutionEventMonitor != null) - { - _solutionEventMonitor.Dispose(); - _solutionEventMonitor = null; - } + protected override void Dispose(bool disposing) + { + UnregisterAnalyzerTracker(); + UnregisterRuleSetEventHandler(); - base.Dispose(disposing); - } + ReportSessionWideTelemetry(); - private void ReportSessionWideTelemetry() + if (_solutionEventMonitor != null) { - AsyncCompletionLogger.ReportTelemetry(); - InheritanceMarginLogger.ReportTelemetry(); - FeaturesSessionTelemetry.Report(); - ComponentModel.GetService().ReportOtherWorkspaceTelemetry(); + _solutionEventMonitor.Dispose(); + _solutionEventMonitor = null; } - private async Task LoadAnalyzerNodeComponentsAsync(CancellationToken cancellationToken) - { - await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); + base.Dispose(disposing); + } - _ruleSetEventHandler = this.ComponentModel.GetService(); - if (_ruleSetEventHandler != null) - await _ruleSetEventHandler.RegisterAsync(this, cancellationToken).ConfigureAwait(false); - } + private void ReportSessionWideTelemetry() + { + AsyncCompletionLogger.ReportTelemetry(); + InheritanceMarginLogger.ReportTelemetry(); + FeaturesSessionTelemetry.Report(); + ComponentModel.GetService().ReportOtherWorkspaceTelemetry(); + } - private void UnregisterAnalyzerTracker() - => this.ComponentModel.GetService().Unregister(); + private async Task LoadAnalyzerNodeComponentsAsync(CancellationToken cancellationToken) + { + await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); - private void UnregisterRuleSetEventHandler() + _ruleSetEventHandler = this.ComponentModel.GetService(); + if (_ruleSetEventHandler != null) + await _ruleSetEventHandler.RegisterAsync(this, cancellationToken).ConfigureAwait(false); + } + + private void UnregisterAnalyzerTracker() + => this.ComponentModel.GetService().Unregister(); + + private void UnregisterRuleSetEventHandler() + { + if (_ruleSetEventHandler != null) { - if (_ruleSetEventHandler != null) - { - _ruleSetEventHandler.Unregister(); - _ruleSetEventHandler = null; - } + _ruleSetEventHandler.Unregister(); + _ruleSetEventHandler = null; } + } - private static void TrackBulkFileOperations(IGlobalOperationNotificationService globalNotificationService) - { - // we will pause whatever ambient work loads we have that are tied to IGlobalOperationNotificationService - // such as solution crawler, preemptive remote host synchronization and etc. any background work users - // didn't explicitly asked for. - // - // this should give all resources to BulkFileOperation. we do same for things like build, debugging, wait - // dialog and etc. BulkFileOperation is used for things like git branch switching and etc. - Contract.ThrowIfNull(globalNotificationService); + private static void TrackBulkFileOperations(IGlobalOperationNotificationService globalNotificationService) + { + // we will pause whatever ambient work loads we have that are tied to IGlobalOperationNotificationService + // such as solution crawler, preemptive remote host synchronization and etc. any background work users + // didn't explicitly asked for. + // + // this should give all resources to BulkFileOperation. we do same for things like build, debugging, wait + // dialog and etc. BulkFileOperation is used for things like git branch switching and etc. + Contract.ThrowIfNull(globalNotificationService); - // BulkFileOperation can't have nested events. there will be ever only 1 events (Begin/End) - // so we only need simple tracking. - var gate = new object(); - IDisposable? localRegistration = null; + // BulkFileOperation can't have nested events. there will be ever only 1 events (Begin/End) + // so we only need simple tracking. + var gate = new object(); + IDisposable? localRegistration = null; - BulkFileOperation.Begin += (s, a) => StartBulkFileOperationNotification(); - BulkFileOperation.End += (s, a) => StopBulkFileOperationNotification(); + BulkFileOperation.Begin += (s, a) => StartBulkFileOperationNotification(); + BulkFileOperation.End += (s, a) => StopBulkFileOperationNotification(); - return; + return; - void StartBulkFileOperationNotification() - { - Contract.ThrowIfNull(gate); - Contract.ThrowIfNull(globalNotificationService); + void StartBulkFileOperationNotification() + { + Contract.ThrowIfNull(gate); + Contract.ThrowIfNull(globalNotificationService); - lock (gate) + lock (gate) + { + // this shouldn't happen, but we are using external component + // so guarding us from them + if (localRegistration != null) { - // this shouldn't happen, but we are using external component - // so guarding us from them - if (localRegistration != null) - { - FatalError.ReportAndCatch(new InvalidOperationException("BulkFileOperation already exist"), ErrorSeverity.General); - return; - } - - localRegistration = globalNotificationService.Start("BulkFileOperation"); + FatalError.ReportAndCatch(new InvalidOperationException("BulkFileOperation already exist"), ErrorSeverity.General); + return; } + + localRegistration = globalNotificationService.Start("BulkFileOperation"); } + } - void StopBulkFileOperationNotification() - { - Contract.ThrowIfNull(gate); - Contract.ThrowIfNull(globalNotificationService); + void StopBulkFileOperationNotification() + { + Contract.ThrowIfNull(gate); + Contract.ThrowIfNull(globalNotificationService); - lock (gate) - { - // localRegistration may be null if BulkFileOperation was already in the middle of running. So we - // explicitly do not assert that is is non-null here. - localRegistration?.Dispose(); - localRegistration = null; - } + lock (gate) + { + // localRegistration may be null if BulkFileOperation was already in the middle of running. So we + // explicitly do not assert that is is non-null here. + localRegistration?.Dispose(); + localRegistration = null; } } } From c229cc8c30e53526e36f1399924f4b0fadca2b8f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:27:53 -0800 Subject: [PATCH 055/151] file scoped --- .../AbstractRoslynTableDataSource.cs | 119 +- .../ExternalErrorDiagnosticUpdateSource.cs | 1681 ++++++++--------- 2 files changed, 899 insertions(+), 901 deletions(-) diff --git a/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs b/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs index c07c38748bb8d..c8bafc62ace46 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/AbstractRoslynTableDataSource.cs @@ -8,84 +8,83 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.SolutionCrawler; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource +namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; + +/// +/// A version of ITableDataSource who knows how to connect them to Roslyn solution crawler for live information. +/// +internal abstract class AbstractRoslynTableDataSource : AbstractTableDataSource + where TItem : TableItem + where TData : notnull { - /// - /// A version of ITableDataSource who knows how to connect them to Roslyn solution crawler for live information. - /// - internal abstract class AbstractRoslynTableDataSource : AbstractTableDataSource - where TItem : TableItem - where TData : notnull - { - public AbstractRoslynTableDataSource(Workspace workspace, IThreadingContext threadingContext) - : base(workspace, threadingContext) - => ConnectToSolutionCrawlerService(workspace); + public AbstractRoslynTableDataSource(Workspace workspace, IThreadingContext threadingContext) + : base(workspace, threadingContext) + => ConnectToSolutionCrawlerService(workspace); - protected ImmutableArray GetDocumentsWithSameFilePath(Solution solution, DocumentId documentId) + protected ImmutableArray GetDocumentsWithSameFilePath(Solution solution, DocumentId documentId) + { + var document = solution.GetTextDocument(documentId); + if (document == null) { - var document = solution.GetTextDocument(documentId); - if (document == null) - { - return ImmutableArray.Empty; - } - - return solution.GetDocumentIdsWithFilePath(document.FilePath); + return ImmutableArray.Empty; } - /// - /// Flag indicating if a solution crawler is running incremental analyzers in background. - /// We get build progress updates from . - /// Solution crawler progress events are guaranteed to be invoked in a serial fashion. - /// - protected bool IsSolutionCrawlerRunning { get; private set; } + return solution.GetDocumentIdsWithFilePath(document.FilePath); + } - private void ConnectToSolutionCrawlerService(Workspace workspace) - { + /// + /// Flag indicating if a solution crawler is running incremental analyzers in background. + /// We get build progress updates from . + /// Solution crawler progress events are guaranteed to be invoked in a serial fashion. + /// + protected bool IsSolutionCrawlerRunning { get; private set; } + + private void ConnectToSolutionCrawlerService(Workspace workspace) + { #if false - var crawlerService = workspace.Services.GetService(); - if (crawlerService == null) - { - // can happen depends on host such as testing host. - return; - } + var crawlerService = workspace.Services.GetService(); + if (crawlerService == null) + { + // can happen depends on host such as testing host. + return; + } - var reporter = crawlerService.GetProgressReporter(workspace); - reporter.ProgressChanged += OnSolutionCrawlerProgressChanged; + var reporter = crawlerService.GetProgressReporter(workspace); + reporter.ProgressChanged += OnSolutionCrawlerProgressChanged; - // set initial value - SolutionCrawlerProgressChanged(reporter.InProgress); + // set initial value + SolutionCrawlerProgressChanged(reporter.InProgress); #endif - } + } #if false - private void OnSolutionCrawlerProgressChanged(object sender, ProgressData progressData) + private void OnSolutionCrawlerProgressChanged(object sender, ProgressData progressData) + { + switch (progressData.Status) { - switch (progressData.Status) - { - case ProgressStatus.Started: - SolutionCrawlerProgressChanged(running: true); - break; - case ProgressStatus.Stopped: - SolutionCrawlerProgressChanged(running: false); - break; - } + case ProgressStatus.Started: + SolutionCrawlerProgressChanged(running: true); + break; + case ProgressStatus.Stopped: + SolutionCrawlerProgressChanged(running: false); + break; } + } #endif - private void SolutionCrawlerProgressChanged(bool running) - { - IsSolutionCrawlerRunning = running; - ChangeStableStateIfRequired(newIsStable: !IsSolutionCrawlerRunning); - } + private void SolutionCrawlerProgressChanged(bool running) + { + IsSolutionCrawlerRunning = running; + ChangeStableStateIfRequired(newIsStable: !IsSolutionCrawlerRunning); + } - protected void ChangeStableStateIfRequired(bool newIsStable) + protected void ChangeStableStateIfRequired(bool newIsStable) + { + var oldIsStable = IsStable; + if (oldIsStable != newIsStable) { - var oldIsStable = IsStable; - if (oldIsStable != newIsStable) - { - IsStable = newIsStable; - ChangeStableState(newIsStable); - } + IsStable = newIsStable; + ChangeStableState(newIsStable); } } } diff --git a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs index 9d12164f5addf..11ddf74c2af7d 100644 --- a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -22,1069 +22,1068 @@ #pragma warning disable CA1200 // Avoid using cref tags with a prefix -namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList +namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; + +using ProjectErrorMap = ImmutableDictionary>; + +/// +/// Diagnostic source for warnings and errors reported from explicit build command invocations in Visual Studio. +/// VS workspaces calls into us when a build is invoked or completed in Visual Studio. +/// calls into us to clear reported diagnostics or to report new diagnostics during the build. +/// For each of these callbacks, we create/capture the current and +/// schedule updating/processing this state on a serialized in the background. +/// The processing phase de-dupes the diagnostics reported from build and intellisense to ensure that the error list does not contain duplicate diagnostics. +/// It raises events about diagnostic updates, which eventually trigger the "Build + Intellisense" and "Build only" error list diagnostic +/// sources to update the reported diagnostics. +/// +internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSource, IDisposable { - using ProjectErrorMap = ImmutableDictionary>; + private readonly Workspace _workspace; + private readonly IDiagnosticAnalyzerService _diagnosticService; + private readonly IBuildOnlyDiagnosticsService _buildOnlyDiagnosticsService; + private readonly IGlobalOperationNotificationService _notificationService; + private readonly CancellationToken _disposalToken; /// - /// Diagnostic source for warnings and errors reported from explicit build command invocations in Visual Studio. - /// VS workspaces calls into us when a build is invoked or completed in Visual Studio. - /// calls into us to clear reported diagnostics or to report new diagnostics during the build. - /// For each of these callbacks, we create/capture the current and - /// schedule updating/processing this state on a serialized in the background. - /// The processing phase de-dupes the diagnostics reported from build and intellisense to ensure that the error list does not contain duplicate diagnostics. - /// It raises events about diagnostic updates, which eventually trigger the "Build + Intellisense" and "Build only" error list diagnostic - /// sources to update the reported diagnostics. + /// Task queue to serialize all the work for errors reported by build. + /// represents the state from build errors, + /// which is built up and processed in serialized fashion on this task queue. /// - internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSource, IDisposable - { - private readonly Workspace _workspace; - private readonly IDiagnosticAnalyzerService _diagnosticService; - private readonly IBuildOnlyDiagnosticsService _buildOnlyDiagnosticsService; - private readonly IGlobalOperationNotificationService _notificationService; - private readonly CancellationToken _disposalToken; - - /// - /// Task queue to serialize all the work for errors reported by build. - /// represents the state from build errors, - /// which is built up and processed in serialized fashion on this task queue. - /// - private readonly TaskQueue _taskQueue; + private readonly TaskQueue _taskQueue; - /// - /// Task queue to serialize all the post-build and post error list refresh tasks. - /// Error list refresh requires build/live diagnostics de-duping to complete, which happens during - /// . - /// Computationally expensive tasks such as writing build errors into persistent storage, - /// invoking background analysis on open files/solution after build completes, etc. - /// are added to this task queue to help ensure faster error list refresh. - /// - private readonly TaskQueue _postBuildAndErrorListRefreshTaskQueue; + /// + /// Task queue to serialize all the post-build and post error list refresh tasks. + /// Error list refresh requires build/live diagnostics de-duping to complete, which happens during + /// . + /// Computationally expensive tasks such as writing build errors into persistent storage, + /// invoking background analysis on open files/solution after build completes, etc. + /// are added to this task queue to help ensure faster error list refresh. + /// + private readonly TaskQueue _postBuildAndErrorListRefreshTaskQueue; - // Gate for concurrent access and fields guarded with this gate. - private readonly object _gate = new(); - private InProgressState? _stateDoNotAccessDirectly; - private readonly CancellationSeries _activeCancellationSeriesDoNotAccessDirectly = new(); + // Gate for concurrent access and fields guarded with this gate. + private readonly object _gate = new(); + private InProgressState? _stateDoNotAccessDirectly; + private readonly CancellationSeries _activeCancellationSeriesDoNotAccessDirectly = new(); - /// - /// Latest diagnostics reported during current or last build. - /// These are not the de-duped build/live diagnostics, but the actual diagnostics from build. - /// They are directly used by the "Build only" error list setting. - /// - private ImmutableArray _lastBuiltResult = ImmutableArray.Empty; - - public ExternalErrorDiagnosticUpdateSource( - VisualStudioWorkspace workspace, - IDiagnosticAnalyzerService diagnosticService, - IDiagnosticUpdateSourceRegistrationService registrationService, - IGlobalOperationNotificationService notificationService, - IAsynchronousOperationListenerProvider listenerProvider, - IThreadingContext threadingContext) - : this(workspace, diagnosticService, notificationService, listenerProvider.GetListener(FeatureAttribute.ErrorList), threadingContext.DisposalToken) - { - registrationService.Register(this); - } + /// + /// Latest diagnostics reported during current or last build. + /// These are not the de-duped build/live diagnostics, but the actual diagnostics from build. + /// They are directly used by the "Build only" error list setting. + /// + private ImmutableArray _lastBuiltResult = ImmutableArray.Empty; + + public ExternalErrorDiagnosticUpdateSource( + VisualStudioWorkspace workspace, + IDiagnosticAnalyzerService diagnosticService, + IDiagnosticUpdateSourceRegistrationService registrationService, + IGlobalOperationNotificationService notificationService, + IAsynchronousOperationListenerProvider listenerProvider, + IThreadingContext threadingContext) + : this(workspace, diagnosticService, notificationService, listenerProvider.GetListener(FeatureAttribute.ErrorList), threadingContext.DisposalToken) + { + registrationService.Register(this); + } - /// - /// internal for testing - /// - internal ExternalErrorDiagnosticUpdateSource( - Workspace workspace, - IDiagnosticAnalyzerService diagnosticService, - IGlobalOperationNotificationService notificationService, - IAsynchronousOperationListener listener, - CancellationToken disposalToken) - { - // use queue to serialize work. no lock needed - _taskQueue = new TaskQueue(listener, TaskScheduler.Default); - _postBuildAndErrorListRefreshTaskQueue = new TaskQueue(listener, TaskScheduler.Default); - _disposalToken = disposalToken; + /// + /// internal for testing + /// + internal ExternalErrorDiagnosticUpdateSource( + Workspace workspace, + IDiagnosticAnalyzerService diagnosticService, + IGlobalOperationNotificationService notificationService, + IAsynchronousOperationListener listener, + CancellationToken disposalToken) + { + // use queue to serialize work. no lock needed + _taskQueue = new TaskQueue(listener, TaskScheduler.Default); + _postBuildAndErrorListRefreshTaskQueue = new TaskQueue(listener, TaskScheduler.Default); + _disposalToken = disposalToken; - _workspace = workspace; - _workspace.WorkspaceChanged += OnWorkspaceChanged; + _workspace = workspace; + _workspace.WorkspaceChanged += OnWorkspaceChanged; - _diagnosticService = diagnosticService; - _buildOnlyDiagnosticsService = _workspace.Services.GetRequiredService(); + _diagnosticService = diagnosticService; + _buildOnlyDiagnosticsService = _workspace.Services.GetRequiredService(); - _notificationService = notificationService; - } + _notificationService = notificationService; + } - public DiagnosticAnalyzerInfoCache AnalyzerInfoCache => _diagnosticService.AnalyzerInfoCache; + public DiagnosticAnalyzerInfoCache AnalyzerInfoCache => _diagnosticService.AnalyzerInfoCache; - /// - /// Event generated from the serialized whenever the build progress in Visual Studio changes. - /// Events are guaranteed to be generated in a serial fashion, but may be invoked on any thread. - /// - public event EventHandler? BuildProgressChanged; + /// + /// Event generated from the serialized whenever the build progress in Visual Studio changes. + /// Events are guaranteed to be generated in a serial fashion, but may be invoked on any thread. + /// + public event EventHandler? BuildProgressChanged; - /// - /// Event generated from the serialized whenever build-only diagnostics are reported during a build in Visual Studio. - /// These diagnostics are not supported from intellisense and only get refreshed during actual build. - /// - public event EventHandler>? DiagnosticsUpdated; + /// + /// Event generated from the serialized whenever build-only diagnostics are reported during a build in Visual Studio. + /// These diagnostics are not supported from intellisense and only get refreshed during actual build. + /// + public event EventHandler>? DiagnosticsUpdated; - /// - /// Event generated from the serialized whenever build-only diagnostics are cleared during a build in Visual Studio. - /// These diagnostics are not supported from intellisense and only get refreshed during actual build. - /// - public event EventHandler DiagnosticsCleared { add { } remove { } } + /// + /// Event generated from the serialized whenever build-only diagnostics are cleared during a build in Visual Studio. + /// These diagnostics are not supported from intellisense and only get refreshed during actual build. + /// + public event EventHandler DiagnosticsCleared { add { } remove { } } - /// - /// Indicates if a build is currently in progress inside Visual Studio. - /// - public bool IsInProgress => GetBuildInProgressState() != null; + /// + /// Indicates if a build is currently in progress inside Visual Studio. + /// + public bool IsInProgress => GetBuildInProgressState() != null; - public void Dispose() + public void Dispose() + { + lock (_gate) { - lock (_gate) - { - _activeCancellationSeriesDoNotAccessDirectly.Dispose(); - } + _activeCancellationSeriesDoNotAccessDirectly.Dispose(); } + } - /// - /// Get the latest diagnostics reported during current or last build. - /// These are not the de-duped build/live diagnostics, but the actual diagnostics from build. - /// They are directly used by the "Build only" error list setting. - /// - public ImmutableArray GetBuildErrors() - => _lastBuiltResult; + /// + /// Get the latest diagnostics reported during current or last build. + /// These are not the de-duped build/live diagnostics, but the actual diagnostics from build. + /// They are directly used by the "Build only" error list setting. + /// + public ImmutableArray GetBuildErrors() + => _lastBuiltResult; - /// - /// Returns true if the given represents an analyzer diagnostic ID that could be reported - /// for the given during the current build in progress. - /// This API is only intended to be invoked from while a build is in progress. - /// - public bool IsSupportedDiagnosticId(ProjectId projectId, string id) - => GetBuildInProgressState()?.IsSupportedDiagnosticId(projectId, id) ?? false; + /// + /// Returns true if the given represents an analyzer diagnostic ID that could be reported + /// for the given during the current build in progress. + /// This API is only intended to be invoked from while a build is in progress. + /// + public bool IsSupportedDiagnosticId(ProjectId projectId, string id) + => GetBuildInProgressState()?.IsSupportedDiagnosticId(projectId, id) ?? false; - private void OnBuildProgressChanged(InProgressState? state, BuildProgress buildProgress) + private void OnBuildProgressChanged(InProgressState? state, BuildProgress buildProgress) + { + if (state != null) { - if (state != null) - { - _lastBuiltResult = state.GetBuildErrors(); - } - - RaiseBuildProgressChanged(buildProgress); + _lastBuiltResult = state.GetBuildErrors(); } - public void ClearErrors(ProjectId projectId) - { - // Capture state if it exists - var state = GetBuildInProgressState(); + RaiseBuildProgressChanged(buildProgress); + } + + public void ClearErrors(ProjectId projectId) + { + // Capture state if it exists + var state = GetBuildInProgressState(); - // Update the state to clear diagnostics and raise corresponding diagnostic updated events - // on a serialized task queue. - _taskQueue.ScheduleTask(nameof(ClearErrors), async () => + // Update the state to clear diagnostics and raise corresponding diagnostic updated events + // on a serialized task queue. + _taskQueue.ScheduleTask(nameof(ClearErrors), async () => + { + if (state == null) + { + // TODO: Is it possible that ClearErrors can be invoked while the build is not in progress? + // We fallback to current solution in the workspace and clear errors for the project. + await ClearErrorsCoreAsync(projectId, _workspace.CurrentSolution, state).ConfigureAwait(false); + } + else { - if (state == null) + // We are going to clear the diagnostics for the current project. + // Additionally, we clear errors for all projects that transitively depend on this project. + // Otherwise, fixing errors in core projects in dependency chain will leave back stale diagnostics in dependent projects. + + // First check if we already cleared the diagnostics for this project when processing a referenced project. + // If so, we don't need to clear diagnostics for it again. + if (state.WereProjectErrorsCleared(projectId)) { - // TODO: Is it possible that ClearErrors can be invoked while the build is not in progress? - // We fallback to current solution in the workspace and clear errors for the project. - await ClearErrorsCoreAsync(projectId, _workspace.CurrentSolution, state).ConfigureAwait(false); + return; } - else - { - // We are going to clear the diagnostics for the current project. - // Additionally, we clear errors for all projects that transitively depend on this project. - // Otherwise, fixing errors in core projects in dependency chain will leave back stale diagnostics in dependent projects. - // First check if we already cleared the diagnostics for this project when processing a referenced project. - // If so, we don't need to clear diagnostics for it again. + var solution = state.Solution; + + await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); + + var transitiveProjectIds = solution.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(projectId); + foreach (var projectId in transitiveProjectIds) + { if (state.WereProjectErrorsCleared(projectId)) { - return; + continue; } - var solution = state.Solution; - await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); - - var transitiveProjectIds = solution.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(projectId); - foreach (var projectId in transitiveProjectIds) - { - if (state.WereProjectErrorsCleared(projectId)) - { - continue; - } - - await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); - } } - }, GetApplicableCancellationToken(state)); + } + }, GetApplicableCancellationToken(state)); - return; + return; - async ValueTask ClearErrorsCoreAsync(ProjectId projectId, Solution solution, InProgressState? state) - { - Debug.Assert(state == null || !state.WereProjectErrorsCleared(projectId)); + async ValueTask ClearErrorsCoreAsync(ProjectId projectId, Solution solution, InProgressState? state) + { + Debug.Assert(state == null || !state.WereProjectErrorsCleared(projectId)); - // Here, we clear the build and live errors for the project. - // Additionally, we mark projects as having its errors cleared. - // This ensures that we do not attempt to clear the diagnostics again for the same project - // when 'ClearErrors' is invoked for multiple dependent projects. - // Finally, we update build progress state so error list gets refreshed. + // Here, we clear the build and live errors for the project. + // Additionally, we mark projects as having its errors cleared. + // This ensures that we do not attempt to clear the diagnostics again for the same project + // when 'ClearErrors' is invoked for multiple dependent projects. + // Finally, we update build progress state so error list gets refreshed. - using (var argsBuilder = TemporaryArray.Empty) - { - AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), solution, projectId); - ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - } + using (var argsBuilder = TemporaryArray.Empty) + { + AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), solution, projectId); + ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); + } - await SetLiveErrorsForProjectAsync(projectId, ImmutableArray.Empty, GetApplicableCancellationToken(state)).ConfigureAwait(false); + await SetLiveErrorsForProjectAsync(projectId, ImmutableArray.Empty, GetApplicableCancellationToken(state)).ConfigureAwait(false); - state?.MarkErrorsCleared(projectId); + state?.MarkErrorsCleared(projectId); - OnBuildProgressChanged(state, BuildProgress.Updated); - } + OnBuildProgressChanged(state, BuildProgress.Updated); } + } - private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) + private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) + { + // Clear relevant build-only errors on workspace events such as solution added/removed/reloaded, + // project added/removed/reloaded, etc. + switch (e.Kind) { - // Clear relevant build-only errors on workspace events such as solution added/removed/reloaded, - // project added/removed/reloaded, etc. - switch (e.Kind) - { - case WorkspaceChangeKind.SolutionAdded: - _taskQueue.ScheduleTask( - "OnSolutionAdded", - () => - { - using var argsBuilder = TemporaryArray.Empty; - foreach (var projectId in e.OldSolution.ProjectIds) - { - AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), e.OldSolution, projectId); - } - - ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - }, - _disposalToken); - break; - - case WorkspaceChangeKind.SolutionRemoved: - case WorkspaceChangeKind.SolutionCleared: - case WorkspaceChangeKind.SolutionReloaded: - _taskQueue.ScheduleTask( - "OnSolutionChanged", - () => - { - using var argsBuilder = TemporaryArray.Empty; - foreach (var projectId in e.OldSolution.ProjectIds) - { - AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), e.OldSolution, projectId); - } - - ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - }, - _disposalToken); - break; - - case WorkspaceChangeKind.ProjectRemoved: - case WorkspaceChangeKind.ProjectReloaded: - _taskQueue.ScheduleTask( - "OnProjectChanged", - () => - { - using var argsBuilder = TemporaryArray.Empty; - AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), e.OldSolution, e.ProjectId); - ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - }, - _disposalToken); - break; - - case WorkspaceChangeKind.DocumentRemoved: - case WorkspaceChangeKind.DocumentReloaded: - case WorkspaceChangeKind.AdditionalDocumentRemoved: - case WorkspaceChangeKind.AdditionalDocumentReloaded: - case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: - case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: - _taskQueue.ScheduleTask( - "OnDocumentRemoved", - () => + case WorkspaceChangeKind.SolutionAdded: + _taskQueue.ScheduleTask( + "OnSolutionAdded", + () => + { + using var argsBuilder = TemporaryArray.Empty; + foreach (var projectId in e.OldSolution.ProjectIds) { - using var argsBuilder = TemporaryArray.Empty; - AddArgsToClearBuildOnlyDocumentErrors(ref argsBuilder.AsRef(), e.OldSolution, e.ProjectId, e.DocumentId); - ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - }, - _disposalToken); - break; - - case WorkspaceChangeKind.DocumentChanged: - case WorkspaceChangeKind.AnalyzerConfigDocumentChanged: - case WorkspaceChangeKind.AdditionalDocumentChanged: - // We clear build-only errors for the document on document edits. - // This is done to address multiple customer reports of stale build-only diagnostics - // after they fix/remove the code flagged from build-only diagnostics, but the diagnostics - // do not get automatically removed/refreshed while typing. - // See https://github.com/dotnet/docs/issues/26708 and https://github.com/dotnet/roslyn/issues/64659 - // for additional details. - _taskQueue.ScheduleTask( - "OnDocumentChanged", - () => + AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), e.OldSolution, projectId); + } + + ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); + }, + _disposalToken); + break; + + case WorkspaceChangeKind.SolutionRemoved: + case WorkspaceChangeKind.SolutionCleared: + case WorkspaceChangeKind.SolutionReloaded: + _taskQueue.ScheduleTask( + "OnSolutionChanged", + () => + { + using var argsBuilder = TemporaryArray.Empty; + foreach (var projectId in e.OldSolution.ProjectIds) { - using var argsBuilder = TemporaryArray.Empty; - AddArgsToClearBuildOnlyDocumentErrors(ref argsBuilder.AsRef(), e.OldSolution, e.ProjectId, e.DocumentId); - ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - }, - _disposalToken); - break; - - case WorkspaceChangeKind.ProjectAdded: - case WorkspaceChangeKind.DocumentAdded: - case WorkspaceChangeKind.ProjectChanged: - case WorkspaceChangeKind.SolutionChanged: - case WorkspaceChangeKind.AdditionalDocumentAdded: - case WorkspaceChangeKind.AnalyzerConfigDocumentAdded: - break; - - default: - throw ExceptionUtilities.UnexpectedValue(e.Kind); - } - } + AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), e.OldSolution, projectId); + } - internal void OnSolutionBuildStarted() - { - // Build just started, create the state and fire build in progress event. - _ = GetOrCreateInProgressState(); + ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); + }, + _disposalToken); + break; + + case WorkspaceChangeKind.ProjectRemoved: + case WorkspaceChangeKind.ProjectReloaded: + _taskQueue.ScheduleTask( + "OnProjectChanged", + () => + { + using var argsBuilder = TemporaryArray.Empty; + AddArgsToClearBuildOnlyProjectErrors(ref argsBuilder.AsRef(), e.OldSolution, e.ProjectId); + ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); + }, + _disposalToken); + break; + + case WorkspaceChangeKind.DocumentRemoved: + case WorkspaceChangeKind.DocumentReloaded: + case WorkspaceChangeKind.AdditionalDocumentRemoved: + case WorkspaceChangeKind.AdditionalDocumentReloaded: + case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: + case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: + _taskQueue.ScheduleTask( + "OnDocumentRemoved", + () => + { + using var argsBuilder = TemporaryArray.Empty; + AddArgsToClearBuildOnlyDocumentErrors(ref argsBuilder.AsRef(), e.OldSolution, e.ProjectId, e.DocumentId); + ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); + }, + _disposalToken); + break; + + case WorkspaceChangeKind.DocumentChanged: + case WorkspaceChangeKind.AnalyzerConfigDocumentChanged: + case WorkspaceChangeKind.AdditionalDocumentChanged: + // We clear build-only errors for the document on document edits. + // This is done to address multiple customer reports of stale build-only diagnostics + // after they fix/remove the code flagged from build-only diagnostics, but the diagnostics + // do not get automatically removed/refreshed while typing. + // See https://github.com/dotnet/docs/issues/26708 and https://github.com/dotnet/roslyn/issues/64659 + // for additional details. + _taskQueue.ScheduleTask( + "OnDocumentChanged", + () => + { + using var argsBuilder = TemporaryArray.Empty; + AddArgsToClearBuildOnlyDocumentErrors(ref argsBuilder.AsRef(), e.OldSolution, e.ProjectId, e.DocumentId); + ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); + }, + _disposalToken); + break; + + case WorkspaceChangeKind.ProjectAdded: + case WorkspaceChangeKind.DocumentAdded: + case WorkspaceChangeKind.ProjectChanged: + case WorkspaceChangeKind.SolutionChanged: + case WorkspaceChangeKind.AdditionalDocumentAdded: + case WorkspaceChangeKind.AnalyzerConfigDocumentAdded: + break; + + default: + throw ExceptionUtilities.UnexpectedValue(e.Kind); } + } - internal void OnSolutionBuildCompleted() - { - // Building is done, so reset the state - // and get local copy of in-progress state. - var inProgressState = ClearInProgressState(); + internal void OnSolutionBuildStarted() + { + // Build just started, create the state and fire build in progress event. + _ = GetOrCreateInProgressState(); + } - // Enqueue build/live sync in the queue. - _taskQueue.ScheduleTask("OnSolutionBuild", async () => + internal void OnSolutionBuildCompleted() + { + // Building is done, so reset the state + // and get local copy of in-progress state. + var inProgressState = ClearInProgressState(); + + // Enqueue build/live sync in the queue. + _taskQueue.ScheduleTask("OnSolutionBuild", async () => + { + try { - try + // nothing to do + if (inProgressState == null) { - // nothing to do - if (inProgressState == null) - { - return; - } + return; + } - // Explicitly start solution crawler if it didn't start yet. since solution crawler is lazy, - // user might have built solution before workspace fires its first event yet (which is when solution crawler is initialized) - // here we give initializeLazily: false so that solution crawler is fully initialized when we do de-dup live and build errors, - // otherwise, we will think none of error we have here belong to live errors since diagnostic service is not initialized yet. + // Explicitly start solution crawler if it didn't start yet. since solution crawler is lazy, + // user might have built solution before workspace fires its first event yet (which is when solution crawler is initialized) + // here we give initializeLazily: false so that solution crawler is fully initialized when we do de-dup live and build errors, + // otherwise, we will think none of error we have here belong to live errors since diagnostic service is not initialized yet. #if false - if (_diagnosticService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - var registrationService = (SolutionCrawlerRegistrationService)_workspace.Services.GetRequiredService(); - registrationService.EnsureRegistration(_workspace, initializeLazily: false); - } + if (_diagnosticService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) + { + var registrationService = (SolutionCrawlerRegistrationService)_workspace.Services.GetRequiredService(); + registrationService.EnsureRegistration(_workspace, initializeLazily: false); + } #endif - // Mark the status as updated to refresh error list before we invoke 'SyncBuildErrorsAndReportAsync', which can take some time to complete. - OnBuildProgressChanged(inProgressState, BuildProgress.Updated); + // Mark the status as updated to refresh error list before we invoke 'SyncBuildErrorsAndReportAsync', which can take some time to complete. + OnBuildProgressChanged(inProgressState, BuildProgress.Updated); - // We are about to update live analyzer data using one from build. - // pause live analyzer - using var operation = _notificationService.Start("BuildDone"); - if (_diagnosticService is DiagnosticAnalyzerService diagnosticService) - await SyncBuildErrorsAndReportOnBuildCompletedAsync(diagnosticService, inProgressState).ConfigureAwait(false); + // We are about to update live analyzer data using one from build. + // pause live analyzer + using var operation = _notificationService.Start("BuildDone"); + if (_diagnosticService is DiagnosticAnalyzerService diagnosticService) + await SyncBuildErrorsAndReportOnBuildCompletedAsync(diagnosticService, inProgressState).ConfigureAwait(false); - // Mark build as complete. - OnBuildProgressChanged(inProgressState, BuildProgress.Done); - } - finally - { - await _postBuildAndErrorListRefreshTaskQueue.LastScheduledTask.ConfigureAwait(false); - } - }, GetApplicableCancellationToken(inProgressState)); - } + // Mark build as complete. + OnBuildProgressChanged(inProgressState, BuildProgress.Done); + } + finally + { + await _postBuildAndErrorListRefreshTaskQueue.LastScheduledTask.ConfigureAwait(false); + } + }, GetApplicableCancellationToken(inProgressState)); + } - /// - /// Core method that de-dupes live and build diagnostics at the completion of build. - /// It raises diagnostic update events for both the Build-only diagnostics and Build + Intellisense diagnostics - /// in the error list. - /// - private ValueTask SyncBuildErrorsAndReportOnBuildCompletedAsync(DiagnosticAnalyzerService diagnosticService, InProgressState inProgressState) + /// + /// Core method that de-dupes live and build diagnostics at the completion of build. + /// It raises diagnostic update events for both the Build-only diagnostics and Build + Intellisense diagnostics + /// in the error list. + /// + private ValueTask SyncBuildErrorsAndReportOnBuildCompletedAsync(DiagnosticAnalyzerService diagnosticService, InProgressState inProgressState) + { + var solution = inProgressState.Solution; + var cancellationToken = inProgressState.CancellationToken; + var (allLiveErrors, pendingLiveErrorsToSync) = inProgressState.GetLiveErrors(); + + // Raise events for build only errors + using var argsBuilder = TemporaryArray.Empty; + var buildErrors = GetBuildErrors().Except(allLiveErrors).GroupBy(k => k.DocumentId); + foreach (var group in buildErrors) { - var solution = inProgressState.Solution; - var cancellationToken = inProgressState.CancellationToken; - var (allLiveErrors, pendingLiveErrorsToSync) = inProgressState.GetLiveErrors(); - - // Raise events for build only errors - using var argsBuilder = TemporaryArray.Empty; - var buildErrors = GetBuildErrors().Except(allLiveErrors).GroupBy(k => k.DocumentId); - foreach (var group in buildErrors) - { - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - if (group.Key == null) + if (group.Key == null) + { + foreach (var projectGroup in group.GroupBy(g => g.ProjectId)) { - foreach (var projectGroup in group.GroupBy(g => g.ProjectId)) - { - Contract.ThrowIfNull(projectGroup.Key); - argsBuilder.Add(CreateArgsToReportBuildErrors(projectGroup.Key, solution, projectGroup.ToImmutableArray())); - } - - continue; + Contract.ThrowIfNull(projectGroup.Key); + argsBuilder.Add(CreateArgsToReportBuildErrors(projectGroup.Key, solution, projectGroup.ToImmutableArray())); } - argsBuilder.Add(CreateArgsToReportBuildErrors(group.Key, solution, group.ToImmutableArray())); + continue; } - ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - - // Report pending live errors - return diagnosticService.SynchronizeWithBuildAsync(_workspace, pendingLiveErrorsToSync, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: true, cancellationToken); + argsBuilder.Add(CreateArgsToReportBuildErrors(group.Key, solution, group.ToImmutableArray())); } - private DiagnosticsUpdatedArgs CreateArgsToReportBuildErrors(T item, Solution solution, ImmutableArray buildErrors) + ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); + + // Report pending live errors + return diagnosticService.SynchronizeWithBuildAsync(_workspace, pendingLiveErrorsToSync, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: true, cancellationToken); + } + + private DiagnosticsUpdatedArgs CreateArgsToReportBuildErrors(T item, Solution solution, ImmutableArray buildErrors) + { + if (item is ProjectId projectId) { - if (item is ProjectId projectId) - { - return CreateDiagnosticsCreatedArgs(projectId, solution, projectId, documentId: null, buildErrors); - } + return CreateDiagnosticsCreatedArgs(projectId, solution, projectId, documentId: null, buildErrors); + } + + RoslynDebug.Assert(item is DocumentId); + var documentId = (DocumentId)(object)item; + return CreateDiagnosticsCreatedArgs(documentId, solution, documentId.ProjectId, documentId, buildErrors); + } + + private void AddArgsToClearBuildOnlyProjectErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId) + { + // Remove all project errors + builder.Add(CreateDiagnosticsRemovedArgs(projectId, solution, projectId, documentId: null)); - RoslynDebug.Assert(item is DocumentId); - var documentId = (DocumentId)(object)item; - return CreateDiagnosticsCreatedArgs(documentId, solution, documentId.ProjectId, documentId, buildErrors); + var project = solution.GetProject(projectId); + if (project == null) + { + return; } - private void AddArgsToClearBuildOnlyProjectErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId) + // Remove all document errors + foreach (var documentId in project.DocumentIds.Concat(project.AdditionalDocumentIds).Concat(project.AnalyzerConfigDocumentIds)) { - // Remove all project errors - builder.Add(CreateDiagnosticsRemovedArgs(projectId, solution, projectId, documentId: null)); + AddArgsToClearBuildOnlyDocumentErrors(ref builder, solution, projectId, documentId); + } + } - var project = solution.GetProject(projectId); - if (project == null) - { - return; - } + private void AddArgsToClearBuildOnlyDocumentErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId, DocumentId? documentId) + => builder.Add(CreateDiagnosticsRemovedArgs(documentId, solution, projectId, documentId)); - // Remove all document errors - foreach (var documentId in project.DocumentIds.Concat(project.AdditionalDocumentIds).Concat(project.AnalyzerConfigDocumentIds)) - { - AddArgsToClearBuildOnlyDocumentErrors(ref builder, solution, projectId, documentId); - } - } + public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic) + { + Debug.Assert(diagnostic.IsBuildDiagnostic()); - private void AddArgsToClearBuildOnlyDocumentErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId, DocumentId? documentId) - => builder.Add(CreateDiagnosticsRemovedArgs(documentId, solution, projectId, documentId)); + // Capture state that will be processed in background thread. + var state = GetOrCreateInProgressState(); - public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic) + _taskQueue.ScheduleTask("Project New Errors", async () => { - Debug.Assert(diagnostic.IsBuildDiagnostic()); + await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); + state.AddError(projectId, diagnostic); + }, state.CancellationToken); + } - // Capture state that will be processed in background thread. - var state = GetOrCreateInProgressState(); + public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic) + { + Debug.Assert(diagnostic.IsBuildDiagnostic()); - _taskQueue.ScheduleTask("Project New Errors", async () => - { - await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); - state.AddError(projectId, diagnostic); - }, state.CancellationToken); - } + // Capture state that will be processed in background thread. + var state = GetOrCreateInProgressState(); - public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic) + _taskQueue.ScheduleTask("Document New Errors", async () => { - Debug.Assert(diagnostic.IsBuildDiagnostic()); + await ReportPreviousProjectErrorsIfRequiredAsync(documentId.ProjectId, state).ConfigureAwait(false); + state.AddError(documentId, diagnostic); + }, state.CancellationToken); + } - // Capture state that will be processed in background thread. - var state = GetOrCreateInProgressState(); + public void AddNewErrors( + ProjectId projectId, HashSet projectErrors, Dictionary> documentErrorMap) + { + Debug.Assert(projectErrors.All(d => d.IsBuildDiagnostic())); + Debug.Assert(documentErrorMap.SelectMany(kvp => kvp.Value).All(d => d.IsBuildDiagnostic())); - _taskQueue.ScheduleTask("Document New Errors", async () => - { - await ReportPreviousProjectErrorsIfRequiredAsync(documentId.ProjectId, state).ConfigureAwait(false); - state.AddError(documentId, diagnostic); - }, state.CancellationToken); - } + // Capture state that will be processed in background thread + var state = GetOrCreateInProgressState(); - public void AddNewErrors( - ProjectId projectId, HashSet projectErrors, Dictionary> documentErrorMap) + _taskQueue.ScheduleTask("Project New Errors", async () => { - Debug.Assert(projectErrors.All(d => d.IsBuildDiagnostic())); - Debug.Assert(documentErrorMap.SelectMany(kvp => kvp.Value).All(d => d.IsBuildDiagnostic())); - - // Capture state that will be processed in background thread - var state = GetOrCreateInProgressState(); + await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); - _taskQueue.ScheduleTask("Project New Errors", async () => - { - await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); + foreach (var kv in documentErrorMap) + state.AddErrors(kv.Key, kv.Value); - foreach (var kv in documentErrorMap) - state.AddErrors(kv.Key, kv.Value); + state.AddErrors(projectId, projectErrors); + }, state.CancellationToken); + } - state.AddErrors(projectId, projectErrors); - }, state.CancellationToken); + /// + /// This method is invoked from all overloads before it adds the new errors to the in progress state. + /// It checks if build reported errors for a different project then the previous callback to report errors. + /// This provides a good checkpoint to de-dupe build and live errors for lastProjectId and + /// raise diagnostic updated events for that project. + /// This ensures that error list keeps getting refreshed while a build is in progress, as opposed to doing all the work + /// and a single refresh when the build completes. + /// + private ValueTask ReportPreviousProjectErrorsIfRequiredAsync(ProjectId projectId, InProgressState state) + { + if (state.TryGetLastProjectWithReportedErrors() is ProjectId lastProjectId && + lastProjectId != projectId) + { + return SetLiveErrorsForProjectAsync(lastProjectId, state); } - /// - /// This method is invoked from all overloads before it adds the new errors to the in progress state. - /// It checks if build reported errors for a different project then the previous callback to report errors. - /// This provides a good checkpoint to de-dupe build and live errors for lastProjectId and - /// raise diagnostic updated events for that project. - /// This ensures that error list keeps getting refreshed while a build is in progress, as opposed to doing all the work - /// and a single refresh when the build completes. - /// - private ValueTask ReportPreviousProjectErrorsIfRequiredAsync(ProjectId projectId, InProgressState state) - { - if (state.TryGetLastProjectWithReportedErrors() is ProjectId lastProjectId && - lastProjectId != projectId) - { - return SetLiveErrorsForProjectAsync(lastProjectId, state); - } + return default; + } - return default; - } + private async ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, InProgressState state) + { + var diagnostics = state.GetLiveErrorsForProject(projectId); + await SetLiveErrorsForProjectAsync(projectId, diagnostics, state.CancellationToken).ConfigureAwait(false); + state.MarkLiveErrorsReported(projectId); + } - private async ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, InProgressState state) + private ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, ImmutableArray diagnostics, CancellationToken cancellationToken) + { + if (_diagnosticService is DiagnosticAnalyzerService diagnosticAnalyzerService) { - var diagnostics = state.GetLiveErrorsForProject(projectId); - await SetLiveErrorsForProjectAsync(projectId, diagnostics, state.CancellationToken).ConfigureAwait(false); - state.MarkLiveErrorsReported(projectId); + // make those errors live errors + var map = ProjectErrorMap.Empty.Add(projectId, diagnostics); + return diagnosticAnalyzerService.SynchronizeWithBuildAsync(_workspace, map, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: false, cancellationToken); } - private ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, ImmutableArray diagnostics, CancellationToken cancellationToken) - { - if (_diagnosticService is DiagnosticAnalyzerService diagnosticAnalyzerService) - { - // make those errors live errors - var map = ProjectErrorMap.Empty.Add(projectId, diagnostics); - return diagnosticAnalyzerService.SynchronizeWithBuildAsync(_workspace, map, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: false, cancellationToken); - } - - return default; - } + return default; + } - private CancellationToken GetApplicableCancellationToken(InProgressState? state) - => state?.CancellationToken ?? _disposalToken; + private CancellationToken GetApplicableCancellationToken(InProgressState? state) + => state?.CancellationToken ?? _disposalToken; - private InProgressState? GetBuildInProgressState() + private InProgressState? GetBuildInProgressState() + { + lock (_gate) { - lock (_gate) - { - return _stateDoNotAccessDirectly; - } + return _stateDoNotAccessDirectly; } + } - private InProgressState? ClearInProgressState() + private InProgressState? ClearInProgressState() + { + lock (_gate) { - lock (_gate) - { - var state = _stateDoNotAccessDirectly; + var state = _stateDoNotAccessDirectly; - _stateDoNotAccessDirectly = null; - return state; - } + _stateDoNotAccessDirectly = null; + return state; } + } - private InProgressState GetOrCreateInProgressState() + private InProgressState GetOrCreateInProgressState() + { + lock (_gate) { - lock (_gate) + if (_stateDoNotAccessDirectly == null) { - if (_stateDoNotAccessDirectly == null) - { - // We take current snapshot of solution when the state is first created. and through out this code, we use this snapshot. - // Since we have no idea what actual snapshot of solution the out of proc build has picked up, it doesn't remove the race we can have - // between build and diagnostic service, but this at least make us to consistent inside of our code. - _stateDoNotAccessDirectly = new InProgressState(this, _workspace.CurrentSolution, _activeCancellationSeriesDoNotAccessDirectly.CreateNext(_disposalToken)); - OnBuildProgressChanged(_stateDoNotAccessDirectly, BuildProgress.Started); - } - - return _stateDoNotAccessDirectly; + // We take current snapshot of solution when the state is first created. and through out this code, we use this snapshot. + // Since we have no idea what actual snapshot of solution the out of proc build has picked up, it doesn't remove the race we can have + // between build and diagnostic service, but this at least make us to consistent inside of our code. + _stateDoNotAccessDirectly = new InProgressState(this, _workspace.CurrentSolution, _activeCancellationSeriesDoNotAccessDirectly.CreateNext(_disposalToken)); + OnBuildProgressChanged(_stateDoNotAccessDirectly, BuildProgress.Started); } - } - private DiagnosticsUpdatedArgs CreateDiagnosticsCreatedArgs(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray items) - { - return DiagnosticsUpdatedArgs.DiagnosticsCreated(CreateArgumentKey(id), _workspace, solution, projectId, documentId, items); + return _stateDoNotAccessDirectly; } + } + + private DiagnosticsUpdatedArgs CreateDiagnosticsCreatedArgs(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray items) + { + return DiagnosticsUpdatedArgs.DiagnosticsCreated(CreateArgumentKey(id), _workspace, solution, projectId, documentId, items); + } - private DiagnosticsUpdatedArgs CreateDiagnosticsRemovedArgs(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId) + private DiagnosticsUpdatedArgs CreateDiagnosticsRemovedArgs(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId) + { + return DiagnosticsUpdatedArgs.DiagnosticsRemoved(CreateArgumentKey(id), _workspace, solution, projectId, documentId); + } + + private void ProcessAndRaiseDiagnosticsUpdated(ImmutableArray argsCollection) + { + if (argsCollection.IsEmpty) { - return DiagnosticsUpdatedArgs.DiagnosticsRemoved(CreateArgumentKey(id), _workspace, solution, projectId, documentId); + return; } - private void ProcessAndRaiseDiagnosticsUpdated(ImmutableArray argsCollection) + foreach (var args in argsCollection) { - if (argsCollection.IsEmpty) + if (args.Kind == DiagnosticsUpdatedKind.DiagnosticsCreated) { - return; + RoslynDebug.AssertNotNull(args.Solution); + _buildOnlyDiagnosticsService.AddBuildOnlyDiagnostics(args.Solution, args.ProjectId, args.DocumentId, args.Diagnostics); } - - foreach (var args in argsCollection) + else if (args.Kind == DiagnosticsUpdatedKind.DiagnosticsRemoved) { - if (args.Kind == DiagnosticsUpdatedKind.DiagnosticsCreated) - { - RoslynDebug.AssertNotNull(args.Solution); - _buildOnlyDiagnosticsService.AddBuildOnlyDiagnostics(args.Solution, args.ProjectId, args.DocumentId, args.Diagnostics); - } - else if (args.Kind == DiagnosticsUpdatedKind.DiagnosticsRemoved) - { - RoslynDebug.AssertNotNull(args.Solution); - _buildOnlyDiagnosticsService.ClearBuildOnlyDiagnostics(args.Solution, args.ProjectId, args.DocumentId); - } + RoslynDebug.AssertNotNull(args.Solution); + _buildOnlyDiagnosticsService.ClearBuildOnlyDiagnostics(args.Solution, args.ProjectId, args.DocumentId); } - - DiagnosticsUpdated?.Invoke(this, argsCollection); } - private static ArgumentKey CreateArgumentKey(object? id) => new(id); + DiagnosticsUpdated?.Invoke(this, argsCollection); + } - private void RaiseBuildProgressChanged(BuildProgress progress) - => BuildProgressChanged?.Invoke(this, progress); + private static ArgumentKey CreateArgumentKey(object? id) => new(id); - #region not supported - public bool SupportGetDiagnostics { get { return false; } } + private void RaiseBuildProgressChanged(BuildProgress progress) + => BuildProgressChanged?.Invoke(this, progress); - public ValueTask> GetDiagnosticsAsync( - Workspace workspace, ProjectId? projectId, DocumentId? documentId, object? id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) - { - return new ValueTask>(ImmutableArray.Empty); - } - #endregion + #region not supported + public bool SupportGetDiagnostics { get { return false; } } - internal TestAccessor GetTestAccessor() - => new(this); + public ValueTask> GetDiagnosticsAsync( + Workspace workspace, ProjectId? projectId, DocumentId? documentId, object? id, bool includeSuppressedDiagnostics = false, CancellationToken cancellationToken = default) + { + return new ValueTask>(ImmutableArray.Empty); + } + #endregion - internal enum BuildProgress - { - Started, - Updated, - Done - } + internal TestAccessor GetTestAccessor() + => new(this); - internal readonly struct TestAccessor(ExternalErrorDiagnosticUpdateSource instance) - { - internal void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) - => instance.OnWorkspaceChanged(sender, e); - } + internal enum BuildProgress + { + Started, + Updated, + Done + } - private sealed class InProgressState - { - private readonly ExternalErrorDiagnosticUpdateSource _owner; - - /// - /// Map from project ID to all the possible analyzer diagnostic IDs that can be reported in the project. - /// - /// - /// This map may be accessed concurrently, so needs to ensure thread safety by using locks. - /// - private readonly Dictionary> _allDiagnosticIdMap = []; - - /// - /// Map from project ID to all the possible intellisense analyzer diagnostic IDs that can be reported in the project. - /// A diagnostic is considered to be an intellise analyzer diagnostic if is reported from a non-compilation end action in an analyzer, - /// i.e. we do not require to analyze the entire compilation to compute these diagnostics. - /// Compilation end diagnostics are considered build-only diagnostics. - /// - /// - /// This map may be accessed concurrently, so needs to ensure thread safety by using locks. - /// - private readonly Dictionary> _liveDiagnosticIdMap = []; - - // Fields that are used only from APIs invoked from serialized task queue, hence don't need to be thread safe. - #region Serialized fields - - /// - /// Map from project ID to a dictionary of reported project level diagnostics to an integral counter. - /// Project level diagnostics are diagnostics that have no document location, i.e. reported with . - /// Integral counter value for each diagnostic is used to order the reported diagnostics in error list - /// based on the order in which they were reported during build. - /// - private readonly Dictionary> _projectMap = []; - - /// - /// Map from document ID to a dictionary of reported document level diagnostics to an integral counter. - /// Project level diagnostics are diagnostics that have a valid document location, i.e. reported with a location within a syntax tree. - /// Integral counter value for each diagnostic is used to order the reported diagnostics in error list - /// based on the order in which they were reported during build. - /// - private readonly Dictionary> _documentMap = []; - - /// - /// Set of projects for which we have already cleared the build and intellisense diagnostics in the error list. - /// - private readonly HashSet _projectsWithErrorsCleared = []; - - /// - /// Set of projects for which we have reported all intellisense/live diagnostics. - /// - private readonly HashSet _projectsWithAllLiveErrorsReported = []; - - /// - /// Set of projects which have at least one project or document diagnostic in - /// and/or . - /// - private readonly HashSet _projectsWithErrors = []; - - /// - /// Last project for which build reported an error through one of the methods. - /// - private ProjectId? _lastProjectWithReportedErrors; - - /// - /// Counter to help order the diagnostics in error list based on the order in which they were reported during build. - /// - private int _incrementDoNotAccessDirectly; - - #endregion - - public InProgressState(ExternalErrorDiagnosticUpdateSource owner, Solution solution, CancellationToken cancellationToken) - { - _owner = owner; - Solution = solution; - CancellationToken = cancellationToken; - } + internal readonly struct TestAccessor(ExternalErrorDiagnosticUpdateSource instance) + { + internal void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) + => instance.OnWorkspaceChanged(sender, e); + } - public Solution Solution { get; } + private sealed class InProgressState + { + private readonly ExternalErrorDiagnosticUpdateSource _owner; - public CancellationToken CancellationToken { get; } + /// + /// Map from project ID to all the possible analyzer diagnostic IDs that can be reported in the project. + /// + /// + /// This map may be accessed concurrently, so needs to ensure thread safety by using locks. + /// + private readonly Dictionary> _allDiagnosticIdMap = []; - private static ImmutableHashSet GetOrCreateDiagnosticIds( - ProjectId projectId, - Dictionary> diagnosticIdMap, - Func> computeDiagosticIds) - { - lock (diagnosticIdMap) - { - if (diagnosticIdMap.TryGetValue(projectId, out var ids)) - { - return ids; - } - } + /// + /// Map from project ID to all the possible intellisense analyzer diagnostic IDs that can be reported in the project. + /// A diagnostic is considered to be an intellise analyzer diagnostic if is reported from a non-compilation end action in an analyzer, + /// i.e. we do not require to analyze the entire compilation to compute these diagnostics. + /// Compilation end diagnostics are considered build-only diagnostics. + /// + /// + /// This map may be accessed concurrently, so needs to ensure thread safety by using locks. + /// + private readonly Dictionary> _liveDiagnosticIdMap = []; - var computedIds = computeDiagosticIds(); + // Fields that are used only from APIs invoked from serialized task queue, hence don't need to be thread safe. + #region Serialized fields - lock (diagnosticIdMap) - { - diagnosticIdMap[projectId] = computedIds; - return computedIds; - } - } + /// + /// Map from project ID to a dictionary of reported project level diagnostics to an integral counter. + /// Project level diagnostics are diagnostics that have no document location, i.e. reported with . + /// Integral counter value for each diagnostic is used to order the reported diagnostics in error list + /// based on the order in which they were reported during build. + /// + private readonly Dictionary> _projectMap = []; - public bool IsSupportedDiagnosticId(ProjectId projectId, string id) - => GetOrCreateSupportedDiagnosticIds(projectId).Contains(id); + /// + /// Map from document ID to a dictionary of reported document level diagnostics to an integral counter. + /// Project level diagnostics are diagnostics that have a valid document location, i.e. reported with a location within a syntax tree. + /// Integral counter value for each diagnostic is used to order the reported diagnostics in error list + /// based on the order in which they were reported during build. + /// + private readonly Dictionary> _documentMap = []; - private ImmutableHashSet GetOrCreateSupportedDiagnosticIds(ProjectId projectId) - { - return GetOrCreateDiagnosticIds(projectId, _allDiagnosticIdMap, ComputeSupportedDiagnosticIds); + /// + /// Set of projects for which we have already cleared the build and intellisense diagnostics in the error list. + /// + private readonly HashSet _projectsWithErrorsCleared = []; - ImmutableHashSet ComputeSupportedDiagnosticIds() - { - var project = Solution.GetProject(projectId); - if (project == null) - { - // projectId no longer exist - return ImmutableHashSet.Empty; - } + /// + /// Set of projects for which we have reported all intellisense/live diagnostics. + /// + private readonly HashSet _projectsWithAllLiveErrorsReported = []; - // set ids set - var builder = ImmutableHashSet.CreateBuilder(); - var descriptorMap = Solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(_owner._diagnosticService.AnalyzerInfoCache, project); - builder.UnionWith(descriptorMap.Values.SelectMany(v => v.Select(d => d.Id))); + /// + /// Set of projects which have at least one project or document diagnostic in + /// and/or . + /// + private readonly HashSet _projectsWithErrors = []; - return builder.ToImmutable(); - } - } + /// + /// Last project for which build reported an error through one of the methods. + /// + private ProjectId? _lastProjectWithReportedErrors; - public ImmutableArray GetBuildErrors() - { - // return errors in the order that is reported - return ImmutableArray.CreateRange( - _projectMap.Values.SelectMany(d => d).Concat(_documentMap.Values.SelectMany(d => d)).OrderBy(kv => kv.Value).Select(kv => kv.Key)); - } + /// + /// Counter to help order the diagnostics in error list based on the order in which they were reported during build. + /// + private int _incrementDoNotAccessDirectly; - public void MarkErrorsCleared(ProjectId projectId) - { - var added = _projectsWithErrorsCleared.Add(projectId); - Debug.Assert(added); - } + #endregion - public bool WereProjectErrorsCleared(ProjectId projectId) - => _projectsWithErrorsCleared.Contains(projectId); + public InProgressState(ExternalErrorDiagnosticUpdateSource owner, Solution solution, CancellationToken cancellationToken) + { + _owner = owner; + Solution = solution; + CancellationToken = cancellationToken; + } - public void MarkLiveErrorsReported(ProjectId projectId) - => _projectsWithAllLiveErrorsReported.Add(projectId); + public Solution Solution { get; } - public ProjectId? TryGetLastProjectWithReportedErrors() - => _lastProjectWithReportedErrors; + public CancellationToken CancellationToken { get; } - public (ImmutableArray allLiveErrors, ProjectErrorMap pendingLiveErrorsToSync) GetLiveErrors() + private static ImmutableHashSet GetOrCreateDiagnosticIds( + ProjectId projectId, + Dictionary> diagnosticIdMap, + Func> computeDiagosticIds) + { + lock (diagnosticIdMap) { - var allLiveErrorsBuilder = ImmutableArray.CreateBuilder(); - var pendingLiveErrorsToSyncBuilder = ImmutableDictionary.CreateBuilder>(); - foreach (var projectId in GetProjectsWithErrors()) + if (diagnosticIdMap.TryGetValue(projectId, out var ids)) { - CancellationToken.ThrowIfCancellationRequested(); - - var errors = GetLiveErrorsForProject(projectId); - allLiveErrorsBuilder.AddRange(errors); - - if (!_projectsWithAllLiveErrorsReported.Contains(projectId)) - { - pendingLiveErrorsToSyncBuilder.Add(projectId, errors); - } + return ids; } + } - return (allLiveErrorsBuilder.ToImmutable(), pendingLiveErrorsToSyncBuilder.ToImmutable()); + var computedIds = computeDiagosticIds(); - // Local functions. - IEnumerable GetProjectsWithErrors() - { - // Filter out project that is no longer exist in IDE - // this can happen if user started a "build" and then remove a project from IDE - // before build finishes - return _projectsWithErrors.Where(p => Solution.GetProject(p) != null); - } + lock (diagnosticIdMap) + { + diagnosticIdMap[projectId] = computedIds; + return computedIds; } + } + + public bool IsSupportedDiagnosticId(ProjectId projectId, string id) + => GetOrCreateSupportedDiagnosticIds(projectId).Contains(id); - public ImmutableArray GetLiveErrorsForProject(ProjectId projectId) + private ImmutableHashSet GetOrCreateSupportedDiagnosticIds(ProjectId projectId) + { + return GetOrCreateDiagnosticIds(projectId, _allDiagnosticIdMap, ComputeSupportedDiagnosticIds); + + ImmutableHashSet ComputeSupportedDiagnosticIds() { var project = Solution.GetProject(projectId); if (project == null) { - return ImmutableArray.Empty; + // projectId no longer exist + return ImmutableHashSet.Empty; } - var diagnostics = _projectMap.Where(kv => kv.Key == projectId).SelectMany(kv => kv.Value).Concat( - _documentMap.Where(kv => kv.Key.ProjectId == projectId).SelectMany(kv => kv.Value)); - using var _ = ArrayBuilder.GetInstance(out var builder); - foreach (var (diagnostic, _) in diagnostics) - { - if (IsLive(project, diagnostic)) - { - builder.Add(diagnostic); - } - } + // set ids set + var builder = ImmutableHashSet.CreateBuilder(); + var descriptorMap = Solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(_owner._diagnosticService.AnalyzerInfoCache, project); + builder.UnionWith(descriptorMap.Values.SelectMany(v => v.Select(d => d.Id))); return builder.ToImmutable(); } + } - public void AddErrors(DocumentId key, HashSet diagnostics) - => AddErrors(_documentMap, key, diagnostics); + public ImmutableArray GetBuildErrors() + { + // return errors in the order that is reported + return ImmutableArray.CreateRange( + _projectMap.Values.SelectMany(d => d).Concat(_documentMap.Values.SelectMany(d => d)).OrderBy(kv => kv.Value).Select(kv => kv.Key)); + } + + public void MarkErrorsCleared(ProjectId projectId) + { + var added = _projectsWithErrorsCleared.Add(projectId); + Debug.Assert(added); + } - public void AddErrors(ProjectId key, HashSet diagnostics) - => AddErrors(_projectMap, key, diagnostics); + public bool WereProjectErrorsCleared(ProjectId projectId) + => _projectsWithErrorsCleared.Contains(projectId); - public void AddError(DocumentId key, DiagnosticData diagnostic) - => AddError(_documentMap, key, diagnostic); + public void MarkLiveErrorsReported(ProjectId projectId) + => _projectsWithAllLiveErrorsReported.Add(projectId); - public void AddError(ProjectId key, DiagnosticData diagnostic) - => AddError(_projectMap, key, diagnostic); + public ProjectId? TryGetLastProjectWithReportedErrors() + => _lastProjectWithReportedErrors; - private bool IsLive(Project project, DiagnosticData diagnosticData) + public (ImmutableArray allLiveErrors, ProjectErrorMap pendingLiveErrorsToSync) GetLiveErrors() + { + var allLiveErrorsBuilder = ImmutableArray.CreateBuilder(); + var pendingLiveErrorsToSyncBuilder = ImmutableDictionary.CreateBuilder>(); + foreach (var projectId in GetProjectsWithErrors()) { - // REVIEW: current design is that we special case compiler analyzer case and we accept only document level - // diagnostic as live. otherwise, we let them be build errors. we changed compiler analyzer accordingly as well - // so that it doesn't report project level diagnostic as live errors. - if (!IsDocumentLevelDiagnostic(diagnosticData) && - diagnosticData.CustomTags.Contains(WellKnownDiagnosticTags.Compiler)) - { - // compiler error but project level error - return false; - } + CancellationToken.ThrowIfCancellationRequested(); - // Compiler diagnostics reported on additional documents indicate mapped diagnostics, such as compiler diagnostics - // in razor files which are actually reported on generated source files but mapped to razor files during build. - // These are not reported on additional files during live analysis, and can be considered to be build-only diagnostics. - if (IsAdditionalDocumentDiagnostic(project, diagnosticData) && - diagnosticData.CustomTags.Contains(WellKnownDiagnosticTags.Compiler)) - { - return false; - } + var errors = GetLiveErrorsForProject(projectId); + allLiveErrorsBuilder.AddRange(errors); - if (IsSupportedLiveDiagnosticId(project, diagnosticData.Id)) + if (!_projectsWithAllLiveErrorsReported.Contains(projectId)) { - return true; + pendingLiveErrorsToSyncBuilder.Add(projectId, errors); } + } - return false; - - static bool IsDocumentLevelDiagnostic(DiagnosticData diagnosticData) - { - if (diagnosticData.DocumentId != null) - { - return true; - } - - // due to mapped file such as - // - // A.cs having - // #line 2 RandomeFile.txt - // ErrorHere - // #line default - // - // we can't simply say it is not document level diagnostic since - // file path is not part of solution. build output will just tell us - // mapped span not original span, so any code like above will not - // part of solution. - // - // but also we can't simply say it is a document level error because it has file path - // since project level error can have a file path pointing to a file such as dll - // , pdb, embedded files and etc. - // - // unfortunately, there is no 100% correct way to do this. - // so we will use a heuristic that will most likely work for most of common cases. - return - !string.IsNullOrEmpty(diagnosticData.DataLocation.UnmappedFileSpan.Path) && - (diagnosticData.DataLocation.UnmappedFileSpan.StartLinePosition.Line > 0 || - diagnosticData.DataLocation.UnmappedFileSpan.StartLinePosition.Character > 0); - } + return (allLiveErrorsBuilder.ToImmutable(), pendingLiveErrorsToSyncBuilder.ToImmutable()); - static bool IsAdditionalDocumentDiagnostic(Project project, DiagnosticData diagnosticData) - => diagnosticData.DocumentId != null && project.ContainsAdditionalDocument(diagnosticData.DocumentId); + // Local functions. + IEnumerable GetProjectsWithErrors() + { + // Filter out project that is no longer exist in IDE + // this can happen if user started a "build" and then remove a project from IDE + // before build finishes + return _projectsWithErrors.Where(p => Solution.GetProject(p) != null); } + } - private bool IsSupportedLiveDiagnosticId(Project project, string id) - => GetOrCreateSupportedLiveDiagnostics(project).Contains(id); + public ImmutableArray GetLiveErrorsForProject(ProjectId projectId) + { + var project = Solution.GetProject(projectId); + if (project == null) + { + return ImmutableArray.Empty; + } - private ImmutableHashSet GetOrCreateSupportedLiveDiagnostics(Project project) + var diagnostics = _projectMap.Where(kv => kv.Key == projectId).SelectMany(kv => kv.Value).Concat( + _documentMap.Where(kv => kv.Key.ProjectId == projectId).SelectMany(kv => kv.Value)); + using var _ = ArrayBuilder.GetInstance(out var builder); + foreach (var (diagnostic, _) in diagnostics) { - var fullSolutionAnalysis = _owner._diagnosticService.GlobalOptions.IsFullSolutionAnalysisEnabled(project.Language); - if (!project.SupportsCompilation || fullSolutionAnalysis) + if (IsLive(project, diagnostic)) { - // Defer to _allDiagnosticIdMap so we avoid placing FSA diagnostics in _liveDiagnosticIdMap - return GetOrCreateSupportedDiagnosticIds(project.Id); + builder.Add(diagnostic); } + } - return GetOrCreateDiagnosticIds(project.Id, _liveDiagnosticIdMap, ComputeSupportedLiveDiagnosticIds); + return builder.ToImmutable(); + } - ImmutableHashSet ComputeSupportedLiveDiagnosticIds() - { - // set ids set - var builder = ImmutableHashSet.CreateBuilder(); - var infoCache = _owner._diagnosticService.AnalyzerInfoCache; + public void AddErrors(DocumentId key, HashSet diagnostics) + => AddErrors(_documentMap, key, diagnostics); - foreach (var analyzersPerReference in project.Solution.SolutionState.Analyzers.CreateDiagnosticAnalyzersPerReference(project)) - { - foreach (var analyzer in analyzersPerReference.Value) - { - var diagnosticIds = infoCache.GetNonCompilationEndDiagnosticDescriptors(analyzer).Select(d => d.Id); - builder.UnionWith(diagnosticIds); - } - } + public void AddErrors(ProjectId key, HashSet diagnostics) + => AddErrors(_projectMap, key, diagnostics); - return builder.ToImmutable(); - } - } + public void AddError(DocumentId key, DiagnosticData diagnostic) + => AddError(_documentMap, key, diagnostic); + + public void AddError(ProjectId key, DiagnosticData diagnostic) + => AddError(_projectMap, key, diagnostic); - private void AddErrors(Dictionary> map, T key, HashSet diagnostics) - where T : notnull + private bool IsLive(Project project, DiagnosticData diagnosticData) + { + // REVIEW: current design is that we special case compiler analyzer case and we accept only document level + // diagnostic as live. otherwise, we let them be build errors. we changed compiler analyzer accordingly as well + // so that it doesn't report project level diagnostic as live errors. + if (!IsDocumentLevelDiagnostic(diagnosticData) && + diagnosticData.CustomTags.Contains(WellKnownDiagnosticTags.Compiler)) { - var errors = GetErrorSet(map, key); - foreach (var diagnostic in diagnostics) - { - AddError(errors, diagnostic, key); - } + // compiler error but project level error + return false; } - private void AddError(Dictionary> map, T key, DiagnosticData diagnostic) - where T : notnull + // Compiler diagnostics reported on additional documents indicate mapped diagnostics, such as compiler diagnostics + // in razor files which are actually reported on generated source files but mapped to razor files during build. + // These are not reported on additional files during live analysis, and can be considered to be build-only diagnostics. + if (IsAdditionalDocumentDiagnostic(project, diagnosticData) && + diagnosticData.CustomTags.Contains(WellKnownDiagnosticTags.Compiler)) { - var errors = GetErrorSet(map, key); - AddError(errors, diagnostic, key); + return false; } - private void AddError(Dictionary errors, DiagnosticData diagnostic, T key) - where T : notnull + if (IsSupportedLiveDiagnosticId(project, diagnosticData.Id)) { - RecordProjectContainsErrors(); + return true; + } - // add only new errors - if (!errors.TryGetValue(diagnostic, out _)) - { - Logger.Log(FunctionId.ExternalErrorDiagnosticUpdateSource_AddError, d => d.ToString(), diagnostic); + return false; - errors.Add(diagnostic, _incrementDoNotAccessDirectly++); + static bool IsDocumentLevelDiagnostic(DiagnosticData diagnosticData) + { + if (diagnosticData.DocumentId != null) + { + return true; } - return; + // due to mapped file such as + // + // A.cs having + // #line 2 RandomeFile.txt + // ErrorHere + // #line default + // + // we can't simply say it is not document level diagnostic since + // file path is not part of solution. build output will just tell us + // mapped span not original span, so any code like above will not + // part of solution. + // + // but also we can't simply say it is a document level error because it has file path + // since project level error can have a file path pointing to a file such as dll + // , pdb, embedded files and etc. + // + // unfortunately, there is no 100% correct way to do this. + // so we will use a heuristic that will most likely work for most of common cases. + return + !string.IsNullOrEmpty(diagnosticData.DataLocation.UnmappedFileSpan.Path) && + (diagnosticData.DataLocation.UnmappedFileSpan.StartLinePosition.Line > 0 || + diagnosticData.DataLocation.UnmappedFileSpan.StartLinePosition.Character > 0); + } - void RecordProjectContainsErrors() - { - RoslynDebug.Assert(key is DocumentId or ProjectId); - var projectId = (key is DocumentId documentId) ? documentId.ProjectId : (ProjectId)(object)key; + static bool IsAdditionalDocumentDiagnostic(Project project, DiagnosticData diagnosticData) + => diagnosticData.DocumentId != null && project.ContainsAdditionalDocument(diagnosticData.DocumentId); + } + + private bool IsSupportedLiveDiagnosticId(Project project, string id) + => GetOrCreateSupportedLiveDiagnostics(project).Contains(id); + + private ImmutableHashSet GetOrCreateSupportedLiveDiagnostics(Project project) + { + var fullSolutionAnalysis = _owner._diagnosticService.GlobalOptions.IsFullSolutionAnalysisEnabled(project.Language); + if (!project.SupportsCompilation || fullSolutionAnalysis) + { + // Defer to _allDiagnosticIdMap so we avoid placing FSA diagnostics in _liveDiagnosticIdMap + return GetOrCreateSupportedDiagnosticIds(project.Id); + } + + return GetOrCreateDiagnosticIds(project.Id, _liveDiagnosticIdMap, ComputeSupportedLiveDiagnosticIds); - // New errors reported for project, need to refresh live errors. - _projectsWithAllLiveErrorsReported.Remove(projectId); + ImmutableHashSet ComputeSupportedLiveDiagnosticIds() + { + // set ids set + var builder = ImmutableHashSet.CreateBuilder(); + var infoCache = _owner._diagnosticService.AnalyzerInfoCache; - if (!_projectsWithErrors.Add(projectId)) + foreach (var analyzersPerReference in project.Solution.SolutionState.Analyzers.CreateDiagnosticAnalyzersPerReference(project)) + { + foreach (var analyzer in analyzersPerReference.Value) { - return; + var diagnosticIds = infoCache.GetNonCompilationEndDiagnosticDescriptors(analyzer).Select(d => d.Id); + builder.UnionWith(diagnosticIds); } - - // this will make build only error list to be updated per project rather than per solution. - // basically this will make errors up to last project to show up in error list - _lastProjectWithReportedErrors = projectId; - _owner.OnBuildProgressChanged(this, BuildProgress.Updated); } - } - private static Dictionary GetErrorSet(Dictionary> map, T key) - where T : notnull - => map.GetOrAdd(key, _ => new Dictionary(DiagnosticDataComparer.Instance)); + return builder.ToImmutable(); + } } - private sealed class ArgumentKey : BuildToolId.Base + private void AddErrors(Dictionary> map, T key, HashSet diagnostics) + where T : notnull { - public ArgumentKey(object? key) : base(key) + var errors = GetErrorSet(map, key); + foreach (var diagnostic in diagnostics) { + AddError(errors, diagnostic, key); } + } + + private void AddError(Dictionary> map, T key, DiagnosticData diagnostic) + where T : notnull + { + var errors = GetErrorSet(map, key); + AddError(errors, diagnostic, key); + } + + private void AddError(Dictionary errors, DiagnosticData diagnostic, T key) + where T : notnull + { + RecordProjectContainsErrors(); - public override string BuildTool + // add only new errors + if (!errors.TryGetValue(diagnostic, out _)) { - get { return PredefinedBuildTools.Build; } + Logger.Log(FunctionId.ExternalErrorDiagnosticUpdateSource_AddError, d => d.ToString(), diagnostic); + + errors.Add(diagnostic, _incrementDoNotAccessDirectly++); } - public override bool Equals(object? obj) - => obj is ArgumentKey && - base.Equals(obj); + return; - public override int GetHashCode() - => base.GetHashCode(); - } + void RecordProjectContainsErrors() + { + RoslynDebug.Assert(key is DocumentId or ProjectId); + var projectId = (key is DocumentId documentId) ? documentId.ProjectId : (ProjectId)(object)key; - private sealed class DiagnosticDataComparer : IEqualityComparer - { - public static readonly DiagnosticDataComparer Instance = new(); + // New errors reported for project, need to refresh live errors. + _projectsWithAllLiveErrorsReported.Remove(projectId); - public bool Equals(DiagnosticData item1, DiagnosticData item2) - { - if ((item1.DocumentId == null) != (item2.DocumentId == null) || - item1.Id != item2.Id || - item1.ProjectId != item2.ProjectId || - item1.Severity != item2.Severity || - item1.Message != item2.Message || - item1.DataLocation.MappedFileSpan.Span != item2.DataLocation.MappedFileSpan.Span || - item1.DataLocation.UnmappedFileSpan.Span != item2.DataLocation.UnmappedFileSpan.Span) + if (!_projectsWithErrors.Add(projectId)) { - return false; + return; } - // TODO: unclear why we are comparing the original paths, and not the normalized paths. This may - // indicate a bug. If it is correct behavior, this should be documented as to why this is the right span - // to be considering. - return (item1.DocumentId != null) - ? item1.DocumentId == item2.DocumentId - : item1.DataLocation.UnmappedFileSpan.Path == item2.DataLocation.UnmappedFileSpan.Path; + // this will make build only error list to be updated per project rather than per solution. + // basically this will make errors up to last project to show up in error list + _lastProjectWithReportedErrors = projectId; + _owner.OnBuildProgressChanged(this, BuildProgress.Updated); } + } + + private static Dictionary GetErrorSet(Dictionary> map, T key) + where T : notnull + => map.GetOrAdd(key, _ => new Dictionary(DiagnosticDataComparer.Instance)); + } + + private sealed class ArgumentKey : BuildToolId.Base + { + public ArgumentKey(object? key) : base(key) + { + } + + public override string BuildTool + { + get { return PredefinedBuildTools.Build; } + } + + public override bool Equals(object? obj) + => obj is ArgumentKey && + base.Equals(obj); + + public override int GetHashCode() + => base.GetHashCode(); + } + + private sealed class DiagnosticDataComparer : IEqualityComparer + { + public static readonly DiagnosticDataComparer Instance = new(); - public int GetHashCode(DiagnosticData obj) + public bool Equals(DiagnosticData item1, DiagnosticData item2) + { + if ((item1.DocumentId == null) != (item2.DocumentId == null) || + item1.Id != item2.Id || + item1.ProjectId != item2.ProjectId || + item1.Severity != item2.Severity || + item1.Message != item2.Message || + item1.DataLocation.MappedFileSpan.Span != item2.DataLocation.MappedFileSpan.Span || + item1.DataLocation.UnmappedFileSpan.Span != item2.DataLocation.UnmappedFileSpan.Span) { - // TODO: unclear on why we're hashing the start of the data location, whereas .Equals above checks the - // full span. - var result = - Hash.Combine(obj.Id, - Hash.Combine(obj.Message, - Hash.Combine(obj.ProjectId, - Hash.Combine(obj.DataLocation.MappedFileSpan.Span.Start.GetHashCode(), - Hash.Combine(obj.DataLocation.UnmappedFileSpan.Span.Start.GetHashCode(), (int)obj.Severity))))); - - // TODO: unclear why we are hashing the original path, and not the normalized path. This may - // indicate a bug. If it is correct behavior, this should be documented as to why this is the right span - // to be considering. - return obj.DocumentId != null - ? Hash.Combine(obj.DocumentId, result) - : Hash.Combine(obj.DataLocation.UnmappedFileSpan.Path, result); + return false; } + + // TODO: unclear why we are comparing the original paths, and not the normalized paths. This may + // indicate a bug. If it is correct behavior, this should be documented as to why this is the right span + // to be considering. + return (item1.DocumentId != null) + ? item1.DocumentId == item2.DocumentId + : item1.DataLocation.UnmappedFileSpan.Path == item2.DataLocation.UnmappedFileSpan.Path; + } + + public int GetHashCode(DiagnosticData obj) + { + // TODO: unclear on why we're hashing the start of the data location, whereas .Equals above checks the + // full span. + var result = + Hash.Combine(obj.Id, + Hash.Combine(obj.Message, + Hash.Combine(obj.ProjectId, + Hash.Combine(obj.DataLocation.MappedFileSpan.Span.Start.GetHashCode(), + Hash.Combine(obj.DataLocation.UnmappedFileSpan.Span.Start.GetHashCode(), (int)obj.Severity))))); + + // TODO: unclear why we are hashing the original path, and not the normalized path. This may + // indicate a bug. If it is correct behavior, this should be documented as to why this is the right span + // to be considering. + return obj.DocumentId != null + ? Hash.Combine(obj.DocumentId, result) + : Hash.Combine(obj.DataLocation.UnmappedFileSpan.Path, result); } } } From 02a6cb2e7d63efbe8b9a4930eafdeb10a5b0afe3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:28:09 -0800 Subject: [PATCH 056/151] file scoped --- .../SolutionCrawler/IIncrementalAnalyzer.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index 1447ae9e3f209..ffd4fce6f68d9 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -8,28 +8,27 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; -namespace Microsoft.CodeAnalysis.SolutionCrawler +namespace Microsoft.CodeAnalysis.SolutionCrawler; + +internal interface IIncrementalAnalyzer { - internal interface IIncrementalAnalyzer - { #if false - Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); - Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); - Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); - Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); - Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); - Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); - Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); + Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); + Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); + Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); + Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); + Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); + Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); + Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); - Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); - Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); + Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); + Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); - Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); - Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); + Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); + Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); - Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); - void LogAnalyzerCountSummary(); - void Shutdown(); + Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); + void LogAnalyzerCountSummary(); + void Shutdown(); #endif - } } From a6341667ee364bf1f2cc4305d52deef9e3145d39 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:39:00 -0800 Subject: [PATCH 057/151] simplify --- .../Core/Interactive/InteractiveWorkspace.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs index c1c0b68f199c3..c34d534cc07a4 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs @@ -19,23 +19,6 @@ internal partial class InteractiveWorkspace : Workspace internal InteractiveWorkspace(HostServices hostServices, IGlobalOptionService globalOptions) : base(hostServices, WorkspaceKind.Interactive) { - // register work coordinator for this workspace -#if false - if (globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - Services.GetRequiredService().Register(this); - } -#endif - } - - protected override void Dispose(bool finalize) - { - // workspace is going away. unregister this workspace from work coordinator -#if false - Services.GetRequiredService().Unregister(this, blockingShutdown: true); -#endif - - base.Dispose(finalize); } public override bool CanOpenDocuments From f0a4e32c2f383aef4af0b6155607ef771d207845 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:39:17 -0800 Subject: [PATCH 058/151] simplify --- .../Core/Preview/AbstractPreviewFactoryService.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs b/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs index 908a83820a713..75a264060fcc5 100644 --- a/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs +++ b/src/EditorFeatures/Core/Preview/AbstractPreviewFactoryService.cs @@ -701,14 +701,6 @@ private async ValueTask> CreateNewDi rightWorkspace = null; }; -#if false - if (_editorOptionsService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - leftWorkspace?.Target.EnableSolutionCrawler(); - rightWorkspace?.Target.EnableSolutionCrawler(); - } -#endif - return CreateDifferenceViewerPreview(diffViewer); } From 8625198d1f9d416807d0b1cde06aebfe709ce307 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:39:32 -0800 Subject: [PATCH 059/151] simplify --- .../Core/Shared/Preview/PreviewWorkspace.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs index 31a630e5aac8b..e238fb9ce7d64 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewWorkspace.cs @@ -32,13 +32,6 @@ public PreviewWorkspace(Solution solution) this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.SolutionChanged, oldSolution, newSolution); } -#if false - public void EnableSolutionCrawler() - { - Services.GetRequiredService().Register(this); - } -#endif - public override bool CanApplyChange(ApplyChangesKind feature) { // one can manipulate preview workspace solution as mush as they want. @@ -109,9 +102,6 @@ protected override void Dispose(bool finalize) { base.Dispose(finalize); -#if false - Services.GetRequiredService().Unregister(this); -#endif ClearSolution(); } } From 362a41c38da756ad1b2df952a044199fcf40e21c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:39:55 -0800 Subject: [PATCH 060/151] simplify --- ...stSolutionCrawlerWorkspaceEventListener.cs | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs diff --git a/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs deleted file mode 100644 index a540acd3b196f..0000000000000 --- a/src/EditorFeatures/Core/SolutionCrawler/HostSolutionCrawlerWorkspaceEventListener.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -[ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class HostSolutionCrawlerWorkspaceEventListener(IGlobalOptionService globalOptions) : IEventListener, IEventListenerStoppable -{ - private readonly IGlobalOptionService _globalOptions = globalOptions; - - public void StartListening(Workspace workspace, object? serviceOpt) - { -#if false - if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - workspace.Services.GetRequiredService().Register(workspace); - } -#endif - } - - public void StopListening(Workspace workspace) - { -#if false - // we do this so that we can stop solution crawler faster and fire some telemetry. - // this is to reduce a case where we keep going even when VS is shutting down since we don't know about that - var registration = workspace.Services.GetRequiredService(); - registration.Unregister(workspace, blockingShutdown: true); -#endif - } -} From ba5f0a61c3f27ed60f84468c2ba4a3c1ecb1b073 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:42:33 -0800 Subject: [PATCH 061/151] simplify --- ...LegacySolutionEventsWorkspaceEventListener.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs index 157e61acda86d..696513ff218eb 100644 --- a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -22,18 +22,15 @@ namespace Microsoft.CodeAnalysis.LegacySolutionEvents; [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] internal sealed partial class HostLegacySolutionEventsWorkspaceEventListener : IEventListener { - private readonly IGlobalOptionService _globalOptions; private readonly IThreadingContext _threadingContext; private readonly AsyncBatchingWorkQueue _eventQueue; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public HostLegacySolutionEventsWorkspaceEventListener( - IGlobalOptionService globalOptions, IThreadingContext threadingContext, IAsynchronousOperationListenerProvider listenerProvider) { - _globalOptions = globalOptions; _threadingContext = threadingContext; _eventQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.Short, @@ -44,16 +41,11 @@ public HostLegacySolutionEventsWorkspaceEventListener( public void StartListening(Workspace workspace, object? serviceOpt) { -#if false - if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) + workspace.WorkspaceChanged += OnWorkspaceChanged; + _threadingContext.DisposalToken.Register(() => { - workspace.WorkspaceChanged += OnWorkspaceChanged; - _threadingContext.DisposalToken.Register(() => - { - workspace.WorkspaceChanged -= OnWorkspaceChanged; - }); - } -#endif + workspace.WorkspaceChanged -= OnWorkspaceChanged; + }); } private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) From bf3639d5ebc610a2c2b4e6a310688293da00dd86 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:43:16 -0800 Subject: [PATCH 062/151] simplify --- src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 17da44ca6b8ee..55b0c9716ad9c 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1074,9 +1074,6 @@ void M() // We enable full solution analysis so the 'AnalyzeDocumentAsync' doesn't skip analysis based on whether the document is active/open. workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); -#if false - await diagnosticIncrementalAnalyzer.AnalyzeDocumentAsync(sourceDocument, bodyOpt: null!, InvocationReasons.DocumentChanged, CancellationToken.None); -#endif await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnosticIncrementalAnalyzer); // Compute and apply code edit @@ -1108,10 +1105,6 @@ void M() if (testWithCachedDiagnostics) { - // Trigger background analysis to ensure analyzer diagnostics are computed and cached. -#if false - await diagnosticIncrementalAnalyzer.AnalyzeDocumentAsync(sourceDocument, bodyOpt: null!, InvocationReasons.DocumentChanged, CancellationToken.None); -#endif await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: !expectedNoFixes, testSpan, diagnosticIncrementalAnalyzer); } From f12a7d35305db9541b529b31943a3343e39d35aa Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:50:24 -0800 Subject: [PATCH 063/151] simplify --- .../DiagnosticAnalyzerServiceTests.cs | 138 ++---------------- 1 file changed, 16 insertions(+), 122 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index ee91f880e0352..a8b551290776e 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -80,7 +80,7 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var exportProvider = workspace.Services.SolutionServices.ExportProvider; Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = service.CreateIncrementalAnalyzer(workspace); + var analyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); var globalOptions = exportProvider.GetExportedValue(); // listen to events @@ -90,8 +90,8 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() Assert.All(a, e => Assert.Empty(e.Diagnostics)); }; - // now call each analyze method. none of them should run. - await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); + await analyzer.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); // wait for all events to raised await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); @@ -204,7 +204,7 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab var exportProvider = workspace.Services.SolutionServices.ExportProvider; Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = service.CreateIncrementalAnalyzer(workspace); + var analyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); // listen to events var syntaxDiagnostic = false; @@ -235,12 +235,9 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab // open document workspace.OpenDocument(document.Id); -#if false - await analyzer.DocumentOpenAsync(document, CancellationToken.None).ConfigureAwait(false); -#endif - // run analysis - await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); + await analyzer.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); // wait for all events to raised await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); @@ -256,13 +253,14 @@ private static async Task TestAnalyzerAsync( Func, (bool, bool)> resultSetter, bool expectedSyntax, bool expectedSemantic) { + _ = document; var exportProvider = workspace.Services.SolutionServices.ExportProvider; Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); var globalOptions = exportProvider.GetExportedValue(); - var analyzer = service.CreateIncrementalAnalyzer(workspace); + var analyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); var syntax = false; var semantic = false; @@ -277,8 +275,8 @@ private static async Task TestAnalyzerAsync( } }; - // now call each analyze method. none of them should run. - await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); + await analyzer.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); // wait for all events to raised await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); @@ -338,20 +336,9 @@ public async Task TestOpenFileOnlyAnalyzerDiagnostics() // open document workspace.OpenDocument(document.Id); -#if false - await analyzer.DocumentOpenAsync(document, CancellationToken.None).ConfigureAwait(false); -#endif - - // cause analysis - await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); // close document workspace.CloseDocument(document.Id); -#if false - await analyzer.DocumentCloseAsync(document, CancellationToken.None).ConfigureAwait(false); -#endif - - await RunAllAnalysisAsync(analyzer, document).ConfigureAwait(false); // wait for all events to raised await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); @@ -463,22 +450,6 @@ public void TestHostAnalyzerOrdering() var service = Assert.IsType(exportProvider.GetExportedValue()); var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); -#if false - var analyzers = incrementalAnalyzer.GetAnalyzersTestOnly(project).ToArray(); - - AssertEx.Equal(new[] - { - typeof(FileContentLoadAnalyzer), - typeof(GeneratorDiagnosticsPlaceholderAnalyzer), - typeof(CSharpCompilerDiagnosticAnalyzer), - typeof(Analyzer), - typeof(Priority0Analyzer), - typeof(Priority1Analyzer), - typeof(Priority10Analyzer), - typeof(Priority15Analyzer), - typeof(Priority20Analyzer) - }, analyzers.Select(a => a.GetType())); -#endif } [Fact] @@ -534,9 +505,6 @@ public async Task TestHostAnalyzerErrorNotLeaking() }; var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); -#if false - await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); -#endif await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.True(called); @@ -644,9 +612,6 @@ private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace }; var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(project.Solution.Workspace); -#if false - await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); -#endif await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.Equal(expectAnalyzerExecuted, called); @@ -705,15 +670,9 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool case BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics: case BackgroundAnalysisScope.OpenFiles: workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); -#if false - await incrementalAnalyzer.AnalyzeNonSourceDocumentAsync(firstAdditionalDocument, InvocationReasons.SyntaxChanged, CancellationToken.None); -#endif break; case BackgroundAnalysisScope.FullSolution: -#if false - await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); -#endif break; default: @@ -826,22 +785,13 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS workspace.OpenDocument(document.Id); var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetService(); documentTrackingService.SetActiveDocument(document.Id); -#if false - await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); -#endif break; case BackgroundAnalysisScope.OpenFiles: workspace.OpenDocument(document.Id); -#if false - await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); -#endif break; case BackgroundAnalysisScope.FullSolution: -#if false - await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); -#endif break; default: @@ -972,9 +922,6 @@ void M() var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetRequiredService(); documentTrackingService.SetActiveDocument(document.Id); -#if false - await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); -#endif break; case BackgroundAnalysisScope.OpenFiles: @@ -983,15 +930,9 @@ void M() else workspace.OpenDocument(document.Id); -#if false - await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); -#endif break; case BackgroundAnalysisScope.FullSolution: -#if false - await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); -#endif break; } @@ -1079,18 +1020,8 @@ void M() Assert.Empty(analyzer.CanceledCompilations); try { - if (actionKind == AnalyzerRegisterActionKind.SyntaxTree) - { -#if false - await incrementalAnalyzer.AnalyzeSyntaxAsync(document, InvocationReasons.SyntaxChanged, analyzer.CancellationToken); -#endif - } - else - { -#if false - await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, analyzer.CancellationToken); -#endif - } + await incrementalAnalyzer.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, analyzer.CancellationToken); throw ExceptionUtilities.Unreachable(); } @@ -1102,18 +1033,8 @@ void M() Assert.Null(diagnostic); // Then invoke analysis without cancellation token, and verify non-cancelled diagnostic. - if (actionKind == AnalyzerRegisterActionKind.SyntaxTree) - { -#if false - await incrementalAnalyzer.AnalyzeSyntaxAsync(document, InvocationReasons.SyntaxChanged, CancellationToken.None); -#endif - } - else - { -#if false - await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); -#endif - } + await incrementalAnalyzer.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); @@ -1327,18 +1248,8 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, }; var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); - if (analyzeProject) - { -#if false - await incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged: true, InvocationReasons.Reanalyze, CancellationToken.None); -#endif - } - else - { -#if false - await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); -#endif - } + await incrementalAnalyzer.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); @@ -1384,23 +1295,6 @@ private static (bool, bool) CompilerAnalyzerResultSetter(bool syntax, bool seman return (syntax, semantic); } - private static async Task RunAllAnalysisAsync(IIncrementalAnalyzer analyzer, TextDocument textDocument) - { -#if false - if (textDocument is Document document) - { - await analyzer.AnalyzeSyntaxAsync(document, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); - await analyzer.AnalyzeDocumentAsync(document, bodyOpt: null, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); - } - else - { - await analyzer.AnalyzeNonSourceDocumentAsync(textDocument, InvocationReasons.Empty, CancellationToken.None).ConfigureAwait(false); - } - - await analyzer.AnalyzeProjectAsync(textDocument.Project, semanticsChanged: true, reasons: InvocationReasons.Empty, cancellationToken: CancellationToken.None).ConfigureAwait(false); -#endif - } - private class Analyzer : DiagnosticAnalyzer { internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); From aad4cc376f2ad047dbf648e961f85df50baedaf9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:50:44 -0800 Subject: [PATCH 064/151] simplify --- src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs index cfd3ccb9b9b86..be8daacdf0597 100644 --- a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs @@ -123,12 +123,6 @@ public void TestPreviewOpenCloseFile() public async Task TestPreviewServices() { using var previewWorkspace = new PreviewWorkspace(EditorTestCompositions.EditorFeatures.GetHostServices()); -#if false - var service = previewWorkspace.Services.GetService(); - var registrationService = Assert.IsType(service); - Assert.False(registrationService.Register(previewWorkspace)); -#endif - var persistentService = previewWorkspace.Services.SolutionServices.GetPersistentStorageService(); await using var storage = await persistentService.GetStorageAsync(SolutionKey.ToSolutionKey(previewWorkspace.CurrentSolution), CancellationToken.None); From beed16602d3b6de08284d18341f8c9272b849039 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:50:55 -0800 Subject: [PATCH 065/151] simplify --- .../SolutionCrawler/WorkCoordinatorTests.cs | 2171 ----------------- 1 file changed, 2171 deletions(-) delete mode 100644 src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs deleted file mode 100644 index 07294c4496bac..0000000000000 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ /dev/null @@ -1,2171 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -#nullable disable - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.Test; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Composition; -using Roslyn.Test.Utilities; -using Roslyn.Utilities; -using Xunit; -using static Microsoft.CodeAnalysis.SolutionCrawler.SolutionCrawlerRegistrationService.WorkCoordinator; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.SolutionCrawler -{ - [UseExportProvider] - public class WorkCoordinatorTests : TestBase - { - private const string SolutionCrawlerWorkspaceKind = "SolutionCrawler"; - - [Fact] - public async Task RegisterService() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind); - - Assert.Empty(workspace.ExportProvider.GetExports()); - var registrationService = Assert.IsType(workspace.Services.GetService()); - - // register and unregister workspace to the service - registrationService.Register(workspace); - registrationService.Unregister(workspace); - - // make sure we wait for all waiter. the test wrongly assumed there won't be - // any pending async event which is implementation detail when creating workspace - // and changing options. - await WaitWaiterAsync(workspace.ExportProvider); - } - - [Fact] - public async Task DynamicallyAddAnalyzer() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind); - // create solution and wait for it to settle - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - // create solution crawler and add new analyzer provider dynamically - Assert.Empty(workspace.ExportProvider.GetExports()); - var service = Assert.IsType(workspace.Services.GetService()); - - service.Register(workspace); - - var worker = new Analyzer(workspace.GlobalOptions); - var provider = new AnalyzerProvider(worker); - - service.AddAnalyzerProvider(provider, metadata: new( - new Dictionary - { - { "WorkspaceKinds", new[] { SolutionCrawlerWorkspaceKind } }, - { "HighPriorityForActiveFile", false }, - { "Name", "TestAnalyzer" } - })); - - // wait for everything to settle - await WaitAsync(service, workspace); - - service.Unregister(workspace); - - // check whether everything ran as expected - Assert.Equal(10, worker.SyntaxDocumentIds.Count); - Assert.Equal(10, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/747226")] - internal async Task SolutionAdded_Simple(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionId = SolutionId.CreateNewId(); - var projectId = ProjectId.CreateNewId(); - - var solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(), - projects: new[] - { - ProjectInfo.Create(projectId, VersionStamp.Create(), "P1", "P1", LanguageNames.CSharp, - documents: new[] - { - DocumentInfo.Create(DocumentId.CreateNewId(projectId), "D1") - }) - }); - - var expectedDocumentEvents = 1; - - var worker = await ExecuteOperationAsync(workspace, w => w.OnSolutionAdded(solutionInfo)); - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task SolutionAdded_Complex(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - - var expectedDocumentEvents = 10; - - var worker = await ExecuteOperationAsync(workspace, w => w.OnSolutionAdded(solution)); - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Solution_Remove(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var worker = await ExecuteOperationAsync(workspace, w => w.OnSolutionRemoved()); - Assert.Equal(10, worker.InvalidateDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Solution_Clear(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var worker = await ExecuteOperationAsync(workspace, w => w.ClearSolution()); - await WaitWaiterAsync(workspace.ExportProvider); - - Assert.Equal(10, worker.InvalidateDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Solution_Reload(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var expectedDocumentEvents = 10; - var expectedProjectEvents = 2; - - var worker = await ExecuteOperationAsync(workspace, w => w.OnSolutionReloaded(solution)); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - Assert.Equal(expectedProjectEvents, worker.ProjectIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Solution_Change(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - await WaitWaiterAsync(workspace.ExportProvider); - - var solution = workspace.CurrentSolution; - var documentId = solution.Projects.First().DocumentIds[0]; - solution = solution.RemoveDocument(documentId); - - var changedSolution = solution.AddProject("P3", "P3", LanguageNames.CSharp).AddDocument("D1", "").Project.Solution; - - var expectedDocumentEvents = 1; - - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeSolutionAsync(changedSolution)); - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Project_Add(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var projectId = ProjectId.CreateNewId(); - var projectInfo = ProjectInfo.Create( - projectId, VersionStamp.Create(), "P3", "P3", LanguageNames.CSharp, - documents: new List - { - DocumentInfo.Create(DocumentId.CreateNewId(projectId), "D1"), - DocumentInfo.Create(DocumentId.CreateNewId(projectId), "D2") - }); - - var expectedDocumentEvents = 2; - - var worker = await ExecuteOperationAsync(workspace, w => w.OnProjectAdded(projectInfo)); - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Project_Remove(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var projectid = workspace.CurrentSolution.ProjectIds[0]; - - var worker = await ExecuteOperationAsync(workspace, w => w.OnProjectRemoved(projectid)); - Assert.Equal(0, worker.SyntaxDocumentIds.Count); - Assert.Equal(5, worker.InvalidateDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Project_Change(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - await WaitWaiterAsync(workspace.ExportProvider); - - var project = workspace.CurrentSolution.Projects.First(); - var documentId = project.DocumentIds[0]; - var solution = workspace.CurrentSolution.RemoveDocument(documentId); - - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, solution)); - Assert.Equal(0, worker.SyntaxDocumentIds.Count); - Assert.Equal(1, worker.InvalidateDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Project_AssemblyName_Change(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - project = project.WithAssemblyName("newName"); - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, project.Solution)); - - var expectedDocumentEvents = 5; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Project_DefaultNamespace_Change(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - project = project.WithDefaultNamespace("newNamespace"); - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, project.Solution)); - - var expectedDocumentEvents = 5; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Project_AnalyzerOptions_Change(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - project = project.AddAdditionalDocument("a1", SourceText.From("")).Project; - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, project.Solution)); - - var expectedDocumentEvents = 5; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - - Assert.Equal(1, worker.NonSourceDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Project_OutputFilePath_Change(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var newSolution = workspace.CurrentSolution.WithProjectOutputFilePath(project.Id, "/newPath"); - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, newSolution)); - - var expectedDocumentEvents = 5; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Project_OutputRefFilePath_Change(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var newSolution = workspace.CurrentSolution.WithProjectOutputRefFilePath(project.Id, "/newPath"); - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, newSolution)); - - var expectedDocumentEvents = 5; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Project_CompilationOutputInfo_Change(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var newSolution = workspace.CurrentSolution.WithProjectCompilationOutputInfo(project.Id, new CompilationOutputInfo(assemblyPath: "/newPath")); - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, newSolution)); - - var expectedDocumentEvents = 5; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Project_RunAnalyzers_Change(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - Assert.True(project.State.RunAnalyzers); - - var newSolution = workspace.CurrentSolution.WithRunAnalyzers(project.Id, false); - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeProjectAsync(project.Id, newSolution)); - - project = workspace.CurrentSolution.GetProject(project.Id); - Assert.False(project.State.RunAnalyzers); - - var expectedDocumentEvents = 5; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - } - - [Fact] - public async Task Test_NeedsReanalysisOnOptionChanged() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - await WaitWaiterAsync(workspace.ExportProvider); - - var worker = await ExecuteOperationAsync(workspace, w => w.GlobalOptions.SetGlobalOption(Analyzer.TestOption, false)); - - Assert.Equal(10, worker.SyntaxDocumentIds.Count); - Assert.Equal(10, worker.DocumentIds.Count); - Assert.Equal(2, worker.ProjectIds.Count); - } - - [Fact] - public async Task Test_BackgroundAnalysisScopeOptionChanged_OpenFiles() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - MakeFirstDocumentActive(workspace.CurrentSolution.Projects.First()); - await WaitWaiterAsync(workspace.ExportProvider); - - Assert.Equal(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, workspace.GlobalOptions.GetBackgroundAnalysisScope(LanguageNames.CSharp)); - - var newAnalysisScope = BackgroundAnalysisScope.OpenFiles; - var worker = await ExecuteOperationAsync(workspace, w => w.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, newAnalysisScope)); - - Assert.Equal(newAnalysisScope, workspace.GlobalOptions.GetBackgroundAnalysisScope(LanguageNames.CSharp)); - Assert.Equal(10, worker.SyntaxDocumentIds.Count); - Assert.Equal(10, worker.DocumentIds.Count); - Assert.Equal(2, worker.ProjectIds.Count); - } - - [Fact] - public async Task Test_BackgroundAnalysisScopeOptionChanged_FullSolution() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solutionInfo = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solutionInfo); - await WaitWaiterAsync(workspace.ExportProvider); - - Assert.Equal(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, workspace.GlobalOptions.GetBackgroundAnalysisScope(LanguageNames.CSharp)); - - var newAnalysisScope = BackgroundAnalysisScope.FullSolution; - var worker = await ExecuteOperationAsync(workspace, w => w.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, newAnalysisScope)); - - Assert.Equal(newAnalysisScope, workspace.GlobalOptions.GetBackgroundAnalysisScope(LanguageNames.CSharp)); - Assert.Equal(10, worker.SyntaxDocumentIds.Count); - Assert.Equal(10, worker.DocumentIds.Count); - Assert.Equal(2, worker.ProjectIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics)] - [InlineData(BackgroundAnalysisScope.OpenFiles)] - [InlineData(BackgroundAnalysisScope.FullSolution)] - [Theory] - internal async Task Project_Reload(BackgroundAnalysisScope analysisScope) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var expectedDocumentEvents = 5; - var expectedProjectEvents = 1; - - var project = solution.Projects[0]; - var worker = await ExecuteOperationAsync(workspace, w => w.OnProjectReloaded(project)); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - Assert.Equal(expectedProjectEvents, worker.ProjectIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Document_Add(BackgroundAnalysisScope analysisScope, bool activeDocument) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var project = workspace.CurrentSolution.Projects.First(p => p.Name == "P1"); - var info = DocumentInfo.Create(DocumentId.CreateNewId(project.Id), "D6"); - - var worker = await ExecuteOperationAsync(workspace, w => - { - w.OnDocumentAdded(info); - - if (activeDocument) - { - var document = w.CurrentSolution.GetDocument(info.Id); - MakeDocumentActive(document); - } - }); - - var expectedDocumentSyntaxEvents = 1; - var expectedDocumentSemanticEvents = 6; - - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Document_Remove(BackgroundAnalysisScope analysisScope, bool removeActiveDocument) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var document = workspace.CurrentSolution.Projects.First().Documents.First(); - if (removeActiveDocument) - { - MakeDocumentActive(document); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var worker = await ExecuteOperationAsync(workspace, w => w.OnDocumentRemoved(document.Id)); - - var expectedDocumentInvalidatedEvents = 1; - var expectedDocumentSyntaxEvents = 0; - var expectedDocumentSemanticEvents = 4; - - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - Assert.Equal(expectedDocumentInvalidatedEvents, worker.InvalidateDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Document_Reload(BackgroundAnalysisScope analysisScope, bool reloadActiveDocument) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var info = solution.Projects[0].Documents[0]; - if (reloadActiveDocument) - { - var document = workspace.CurrentSolution.GetDocument(info.Id); - MakeDocumentActive(document); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var worker = await ExecuteOperationAsync(workspace, w => w.OnDocumentReloaded(info)); - Assert.Equal(0, worker.SyntaxDocumentIds.Count); - Assert.Equal(0, worker.DocumentIds.Count); - Assert.Equal(0, worker.InvalidateDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Document_Reanalyze(BackgroundAnalysisScope analysisScope, bool reanalyzeActiveDocument) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var info = solution.Projects[0].Documents[0]; - if (reanalyzeActiveDocument) - { - var document = workspace.CurrentSolution.GetDocument(info.Id); - MakeDocumentActive(document); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var lazyWorker = Assert.Single(workspace.ExportProvider.GetExports()); - VerifyMetadata(lazyWorker.Metadata); - var worker = Assert.IsType(Assert.IsAssignableFrom(lazyWorker.Value).Analyzer); - Assert.False(worker.WaitForCancellation); - Assert.False(worker.BlockedRun); - var service = Assert.IsType(workspace.Services.GetService()); - - service.Register(workspace); - - // don't rely on background parser to have tree. explicitly do it here. - await TouchEverything(workspace.CurrentSolution); - - service.Reanalyze(workspace, worker, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(info.Id), highPriority: false); - - await TouchEverything(workspace.CurrentSolution); - - await WaitAsync(service, workspace); - - service.Unregister(workspace); - - var expectedReanalyzeDocumentCount = 1; - - Assert.Equal(expectedReanalyzeDocumentCount, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedReanalyzeDocumentCount, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/670335")] - internal async Task Document_Change(BackgroundAnalysisScope analysisScope, bool changeActiveDocument) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var document = workspace.CurrentSolution.Projects.First().Documents.First(); - if (changeActiveDocument) - { - MakeDocumentActive(document); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var worker = await ExecuteOperationAsync(workspace, w => w.ChangeDocumentAsync(document.Id, SourceText.From("//"))); - - var expectedDocumentEvents = 1; - - Assert.Equal(expectedDocumentEvents, worker.SyntaxDocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Document_AdditionalFileChange(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var project = workspace.CurrentSolution.Projects.First(); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var expectedDocumentSyntaxEvents = 5; - var expectedDocumentSemanticEvents = 5; - var expectedNonSourceDocumentEvents = 1; - - var ncfile = DocumentInfo.Create(DocumentId.CreateNewId(project.Id), "D6"); - - var worker = await ExecuteOperationAsync(workspace, w => w.OnAdditionalDocumentAdded(ncfile)); - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - Assert.Equal(expectedNonSourceDocumentEvents, worker.NonSourceDocumentIds.Count); - - worker = await ExecuteOperationAsync(workspace, w => w.ChangeAdditionalDocument(ncfile.Id, SourceText.From("//"))); - - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - Assert.Equal(expectedNonSourceDocumentEvents, worker.NonSourceDocumentIds.Count); - - worker = await ExecuteOperationAsync(workspace, w => w.OnAdditionalDocumentRemoved(ncfile.Id)); - - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - Assert.Empty(worker.NonSourceDocumentIds); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory] - internal async Task Document_AnalyzerConfigFileChange(BackgroundAnalysisScope analysisScope, bool firstDocumentActive) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var project = workspace.CurrentSolution.Projects.First(); - if (firstDocumentActive) - { - MakeFirstDocumentActive(project); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var expectedDocumentSyntaxEvents = 5; - var expectedDocumentSemanticEvents = 5; - - var analyzerConfigDocFilePath = PathUtilities.CombineAbsoluteAndRelativePaths(Temp.CreateDirectory().Path, ".editorconfig"); - var analyzerConfigFile = DocumentInfo.Create(DocumentId.CreateNewId(project.Id), ".editorconfig", filePath: analyzerConfigDocFilePath); - - var worker = await ExecuteOperationAsync(workspace, w => w.OnAnalyzerConfigDocumentAdded(analyzerConfigFile)); - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - - worker = await ExecuteOperationAsync(workspace, w => w.ChangeAnalyzerConfigDocument(analyzerConfigFile.Id, SourceText.From("//"))); - - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - - worker = await ExecuteOperationAsync(workspace, w => w.OnAnalyzerConfigDocumentRemoved(analyzerConfigFile.Id)); - - Assert.Equal(expectedDocumentSyntaxEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, worker.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/670335")] - internal async Task Document_Cancellation(BackgroundAnalysisScope analysisScope, bool activeDocument) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var document = workspace.CurrentSolution.Projects.First().Documents.First(); - if (activeDocument) - { - MakeDocumentActive(document); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var lazyWorker = Assert.Single(workspace.ExportProvider.GetExports()); - VerifyMetadata(lazyWorker.Metadata); - var analyzer = Assert.IsType(Assert.IsAssignableFrom(lazyWorker.Value).Analyzer); - Assert.True(analyzer.WaitForCancellation); - Assert.False(analyzer.BlockedRun); - var service = Assert.IsType(workspace.Services.GetService()); - - service.Register(workspace); - - var expectedDocumentSyntaxEvents = 1; - var expectedDocumentSemanticEvents = 5; - - var listenerProvider = GetListenerProvider(workspace.ExportProvider); - - // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test - var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy).BeginAsyncOperation("Test operation"); - var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawlerLegacy).ExpeditedWaitAsync(); - - await workspace.ChangeDocumentAsync(document.Id, SourceText.From("//")); - if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) - { - analyzer.RunningEvent.Wait(); - } - - token.Dispose(); - - await workspace.ChangeDocumentAsync(document.Id, SourceText.From("// ")); - await WaitAsync(service, workspace); - await expeditedWait; - - service.Unregister(workspace); - - Assert.Equal(expectedDocumentSyntaxEvents, analyzer.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, analyzer.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/670335")] - internal async Task Document_Cancellation_MultipleTimes(BackgroundAnalysisScope analysisScope, bool activeDocument) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - var document = workspace.CurrentSolution.Projects.First().Documents.First(); - if (activeDocument) - { - MakeDocumentActive(document); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var expectedDocumentSyntaxEvents = 1; - var expectedDocumentSemanticEvents = 5; - - var lazyWorker = Assert.Single(workspace.ExportProvider.GetExports()); - VerifyMetadata(lazyWorker.Metadata); - var analyzer = Assert.IsType(Assert.IsAssignableFrom(lazyWorker.Value).Analyzer); - Assert.True(analyzer.WaitForCancellation); - Assert.False(analyzer.BlockedRun); - var service = Assert.IsType(workspace.Services.GetService()); - - service.Register(workspace); - - var listenerProvider = GetListenerProvider(workspace.ExportProvider); - - // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test - var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy).BeginAsyncOperation("Test operation"); - var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawlerLegacy).ExpeditedWaitAsync(); - - await workspace.ChangeDocumentAsync(document.Id, SourceText.From("//")); - if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) - { - analyzer.RunningEvent.Wait(); - analyzer.RunningEvent.Reset(); - } - - await workspace.ChangeDocumentAsync(document.Id, SourceText.From("// ")); - if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) - { - analyzer.RunningEvent.Wait(); - } - - token.Dispose(); - - await workspace.ChangeDocumentAsync(document.Id, SourceText.From("// ")); - await WaitAsync(service, workspace); - await expeditedWait; - - service.Unregister(workspace); - - Assert.Equal(expectedDocumentSyntaxEvents, analyzer.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentSemanticEvents, analyzer.DocumentIds.Count); - } - - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/21082"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/670335")] - public async Task Document_InvocationReasons() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var id = workspace.CurrentSolution.Projects.First().DocumentIds[0]; - - var lazyWorker = Assert.Single(workspace.ExportProvider.GetExports()); - VerifyMetadata(lazyWorker.Metadata); - var analyzer = Assert.IsType(Assert.IsAssignableFrom(lazyWorker.Value).Analyzer); - Assert.False(analyzer.WaitForCancellation); - Assert.True(analyzer.BlockedRun); - var service = Assert.IsType(workspace.Services.GetService()); - - service.Register(workspace); - - // first invocation will block worker - await workspace.ChangeDocumentAsync(id, SourceText.From("//")); - analyzer.RunningEvent.Wait(); - - var openReady = new ManualResetEventSlim(initialState: false); - var closeReady = new ManualResetEventSlim(initialState: false); - - workspace.DocumentOpened += (o, e) => openReady.Set(); - workspace.DocumentClosed += (o, e) => closeReady.Set(); - - // cause several different request to queue up - _ = workspace.ChangeDocumentAsync(id, SourceText.From("// ")); - workspace.OpenDocument(id); - workspace.CloseDocument(id); - - openReady.Set(); - closeReady.Set(); - analyzer.BlockEvent.Set(); - - await WaitAsync(service, workspace); - - service.Unregister(workspace); - - Assert.Equal(1, analyzer.SyntaxDocumentIds.Count); - Assert.Equal(5, analyzer.DocumentIds.Count); - } - - [InlineData(BackgroundAnalysisScope.None, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, false)] - [InlineData(BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, true)] - [InlineData(BackgroundAnalysisScope.OpenFiles, false)] - [InlineData(BackgroundAnalysisScope.FullSolution, false)] - [Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/670335")] - internal async Task Document_ActiveDocumentChanged(BackgroundAnalysisScope analysisScope, bool hasActiveDocumentBefore) - { - using var workspace = WorkCoordinatorWorkspace.CreateWithAnalysisScope(analysisScope, SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - var solution = GetInitialSolutionInfo_2Projects_10Documents(); - workspace.OnSolutionAdded(solution); - - var documents = workspace.CurrentSolution.Projects.First().Documents.ToArray(); - var firstDocument = documents[0]; - var secondDocument = documents[1]; - if (hasActiveDocumentBefore) - { - MakeDocumentActive(firstDocument); - } - - await WaitWaiterAsync(workspace.ExportProvider); - - var expectedSyntaxDocumentEvents = (analysisScope, hasActiveDocumentBefore) switch - { - (BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, _) => 1, - (BackgroundAnalysisScope.OpenFiles or BackgroundAnalysisScope.FullSolution or BackgroundAnalysisScope.None, _) => 0, - _ => throw ExceptionUtilities.Unreachable(), - }; - - var expectedDocumentEvents = (analysisScope, hasActiveDocumentBefore) switch - { - (BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, _) => 1, - (BackgroundAnalysisScope.OpenFiles or BackgroundAnalysisScope.FullSolution or BackgroundAnalysisScope.None, _) => 0, - _ => throw ExceptionUtilities.Unreachable(), - }; - - // Switch to another active source document and verify expected document analysis callbacks - var worker = await ExecuteOperationAsync(workspace, w => MakeDocumentActive(secondDocument)); - Assert.Equal(expectedSyntaxDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - Assert.Equal(0, worker.InvalidateDocumentIds.Count); - - // Switch from an active source document to an active non-source document and verify no document analysis callbacks - worker = await ExecuteOperationAsync(workspace, w => ClearActiveDocument(w)); - Assert.Equal(0, worker.SyntaxDocumentIds.Count); - Assert.Equal(0, worker.DocumentIds.Count); - Assert.Equal(0, worker.InvalidateDocumentIds.Count); - - // Switch from an active non-source document to an active source document and verify document analysis callbacks - worker = await ExecuteOperationAsync(workspace, w => MakeDocumentActive(firstDocument)); - Assert.Equal(expectedSyntaxDocumentEvents, worker.SyntaxDocumentIds.Count); - Assert.Equal(expectedDocumentEvents, worker.DocumentIds.Count); - Assert.Equal(0, worker.InvalidateDocumentIds.Count); - } - - [Fact] - public async Task DocumentOpenedClosedEvents() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - - var document = new EditorTestHostDocument(); - var project = new EditorTestHostProject(workspace, document); - workspace.AddTestProject(project); - - await WaitWaiterAsync(workspace.ExportProvider); - - var docOpened = false; - var docClosed = false; - var textDocOpened = false; - var textDocClosed = false; - - workspace.DocumentOpened += (o, e) => docOpened = true; - workspace.DocumentClosed += (o, e) => docClosed = true; - - workspace.TextDocumentOpened += (o, e) => textDocOpened = true; - workspace.TextDocumentClosed += (o, e) => textDocClosed = true; - - var id = workspace.Documents.First().Id; - var worker = await ExecuteOperationAsync(workspace, w => w.OpenDocument(id)); - Assert.True(docOpened); - Assert.True(textDocOpened); - Assert.Equal(1, worker.OpenedDocumentIds.Count); - - worker = await ExecuteOperationAsync(workspace, w => w.CloseDocument(id)); - Assert.True(docClosed); - Assert.True(textDocClosed); - - Assert.Equal(1, worker.ClosedDocumentIds.Count); - } - - [Fact] - public async Task AdditionalDocumentOpenedClosedEvents() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - - var document = new EditorTestHostDocument(); - var project = new EditorTestHostProject(workspace, additionalDocuments: new[] { document }); - workspace.AddTestProject(project); - - await WaitWaiterAsync(workspace.ExportProvider); - - var opened = false; - var closed = false; - - workspace.TextDocumentOpened += (o, e) => opened = true; - workspace.TextDocumentClosed += (o, e) => closed = true; - - var id = workspace.AdditionalDocuments.First().Id; - var worker = await ExecuteOperationAsync(workspace, w => w.OpenAdditionalDocument(id)); - Assert.True(opened); - Assert.Equal(1, worker.OpenedNonSourceDocumentIds.Count); - - worker = await ExecuteOperationAsync(workspace, w => w.CloseAdditionalDocument(id)); - Assert.True(closed); - // TODO: Below check seems to fail occassionally. We should investigate and re-enable it. - //Assert.Equal(1, worker.ClosedNonSourceDocumentIds.Count); - } - - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/62479")] - public async Task AnalyzerConfigDocumentOpenedClosedEvents() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - - var document = new EditorTestHostDocument(); - var project = new EditorTestHostProject(workspace, analyzerConfigDocuments: new[] { document }); - workspace.AddTestProject(project); - - await WaitWaiterAsync(workspace.ExportProvider); - - var opened = false; - var closed = false; - - workspace.TextDocumentOpened += (o, e) => opened = true; - workspace.TextDocumentClosed += (o, e) => closed = true; - - var id = workspace.AnalyzerConfigDocuments.First().Id; - var worker = await ExecuteOperationAsync(workspace, w => w.OpenAnalyzerConfigDocument(id)); - Assert.True(opened); - Assert.Equal(1, worker.OpenedNonSourceDocumentIds.Count); - - worker = await ExecuteOperationAsync(workspace, w => w.CloseAnalyzerConfigDocument(id)); - Assert.True(closed); - // TODO: Below check seems to fail occassionally. We should investigate and re-enable it. - //Assert.Equal(1, worker.ClosedNonSourceDocumentIds.Count); - } - - [Fact] - public async Task Document_TopLevelType_Whitespace() - { - var code = @"class C { $$ }"; - var textToInsert = " "; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_TopLevelType_Character() - { - var code = @"class C { $$ }"; - var textToInsert = "int"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_TopLevelType_NewLine() - { - var code = @"class C { $$ }"; - var textToInsert = "\r\n"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_TopLevelType_NewLine2() - { - var code = @"class C { $$"; - var textToInsert = "\r\n"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_EmptyFile() - { - var code = @"$$"; - var textToInsert = "class"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_TopLevel1() - { - var code = @"class C -{ - public void Test($$"; - var textToInsert = "int"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_TopLevel2() - { - var code = @"class C -{ - public void Test(int $$"; - var textToInsert = " "; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_TopLevel3() - { - var code = @"class C -{ - public void Test(int i,$$"; - var textToInsert = "\r\n"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_InteriorNode1() - { - var code = @"class C -{ - public void Test() - {$$"; - var textToInsert = "\r\n"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: false); - } - - [Fact] - public async Task Document_InteriorNode2() - { - var code = @"class C -{ - public void Test() - { - $$ - }"; - var textToInsert = "int"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: false); - } - - [Fact] - public async Task Document_InteriorNode_Field() - { - var code = @"class C -{ - int i = $$ -}"; - var textToInsert = "1"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: false); - } - - [Fact] - public async Task Document_InteriorNode_Field1() - { - var code = @"class C -{ - int i = 1 + $$ -}"; - var textToInsert = "1"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: false); - } - - [Fact] - public async Task Document_InteriorNode_Accessor() - { - var code = @"class C -{ - public int A - { - get - { - $$ - } - } -}"; - var textToInsert = "return"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: false); - } - - [Fact] - public async Task Document_TopLevelWhitespace() - { - var code = @"class C -{ - /// $$ - public int A() - { - } -}"; - var textToInsert = "return"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_TopLevelWhitespace2() - { - var code = @"/// $$ -class C -{ - public int A() - { - } -}"; - var textToInsert = "return"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact] - public async Task Document_InteriorNode_Malformed() - { - var code = @"class C -{ - public void Test() - { - $$"; - var textToInsert = "int"; - - await InsertText(code, textToInsert, expectDocumentAnalysis: true); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/739943")] - public async Task SemanticChange_Propagation_Direct() - { - var solution = GetInitialSolutionInfoWithP2P(); - - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProviderNoWaitNoBlock)); - workspace.OnSolutionAdded(solution); - await WaitWaiterAsync(workspace.ExportProvider); - - var id = solution.Projects[0].Id; - var info = DocumentInfo.Create(DocumentId.CreateNewId(id), "D6"); - - var worker = await ExecuteOperationAsync(workspace, w => w.OnDocumentAdded(info)); - - Assert.Equal(1, worker.SyntaxDocumentIds.Count); - Assert.Equal(3, worker.DocumentIds.Count); - } - - [Fact] - public async Task SpecificAnalyzers_AllThenSpecific() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind); - await WaitWaiterAsync(workspace.ExportProvider); - - var projectId = ProjectId.CreateNewId(); - var documentId = DocumentId.CreateNewId(projectId); - - var analyzer = new Analyzer(workspace.GlobalOptions); - var analyzer2 = new Analyzer2(); - var allAnalyzers = ImmutableArray.Create(analyzer, analyzer2); - - var item = new WorkItem(documentId, "C#", InvocationReasons.DocumentAdded, isLowPriority: false, analyzer: null, EmptyAsyncToken.Instance); - - item = item.With(item.InvocationReasons, item.ActiveMember, specificAnalyzers: ImmutableHashSet.Create(analyzer2), item.AsyncToken); - - Assert.True(item.SpecificAnalyzers.IsEmpty); - Assert.Equal(2, item.GetApplicableAnalyzers(allAnalyzers).Count()); - } - - [Fact] - public async Task SpecificAnalyzers_SpecificThenAll() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind); - await WaitWaiterAsync(workspace.ExportProvider); - - var projectId = ProjectId.CreateNewId(); - var documentId = DocumentId.CreateNewId(projectId); - - var analyzer = new Analyzer(workspace.GlobalOptions); - var analyzer2 = new Analyzer2(); - var allAnalyzers = ImmutableArray.Create(analyzer, analyzer2); - - var item = new WorkItem(documentId, "C#", InvocationReasons.DocumentAdded, isLowPriority: false, analyzer: analyzer, EmptyAsyncToken.Instance); - - item = item.With(item.InvocationReasons, item.ActiveMember, specificAnalyzers: ImmutableHashSet.Empty, item.AsyncToken); - - Assert.True(item.SpecificAnalyzers.IsEmpty); - Assert.Equal(2, item.GetApplicableAnalyzers(allAnalyzers).Count()); - } - - [Fact] - public async Task SpecificAnalyzers_TwoSpecific() - { - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind); - await WaitWaiterAsync(workspace.ExportProvider); - - var projectId = ProjectId.CreateNewId(); - var documentId = DocumentId.CreateNewId(projectId); - - var analyzer = new Analyzer(workspace.GlobalOptions); - var analyzer2 = new Analyzer2(); - var allAnalyzers = ImmutableArray.Create(analyzer, analyzer2); - - var item = new WorkItem(documentId, "C#", InvocationReasons.DocumentAdded, isLowPriority: false, analyzer: analyzer, EmptyAsyncToken.Instance); - - item = item.With(item.InvocationReasons, item.ActiveMember, specificAnalyzers: ImmutableHashSet.Create(analyzer2), item.AsyncToken); - - Assert.Equal(2, item.SpecificAnalyzers.Count); - Assert.Equal(2, item.GetApplicableAnalyzers(allAnalyzers).Count()); - } - - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/23657")] - public async Task ProgressReporterTest() - { - var solution = GetInitialSolutionInfoWithP2P(); - - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind); - await WaitWaiterAsync(workspace.ExportProvider); - - var service = workspace.Services.GetService(); - var reporter = service.GetProgressReporter(workspace); - Assert.False(reporter.InProgress); - - // set up events - var started = false; - var stopped = false; - - reporter.ProgressChanged += (o, s) => - { - if (s.Status == ProgressStatus.Started) - { - started = true; - } - else if (s.Status == ProgressStatus.Stopped) - { - stopped = true; - } - }; - - var registrationService = workspace.Services.GetService(); - registrationService.Register(workspace); - - // first mutation - workspace.OnSolutionAdded(solution); - - await WaitAsync((SolutionCrawlerRegistrationService)registrationService, workspace); - - Assert.True(started); - Assert.True(stopped); - - // reset - started = false; - stopped = false; - - // second mutation - workspace.OnDocumentAdded(DocumentInfo.Create(DocumentId.CreateNewId(solution.Projects[0].Id), "D6")); - - await WaitAsync((SolutionCrawlerRegistrationService)registrationService, workspace); - - Assert.True(started); - Assert.True(stopped); - - registrationService.Unregister(workspace); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26244")] - public async Task FileFromSameProjectTogetherTest() - { - var projectId1 = ProjectId.CreateNewId(); - var projectId2 = ProjectId.CreateNewId(); - var projectId3 = ProjectId.CreateNewId(); - - var solution = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(), - projects: new[] - { - ProjectInfo.Create(projectId1, VersionStamp.Create(), "P1", "P1", LanguageNames.CSharp, - documents: GetDocuments(projectId1, count: 5)), - ProjectInfo.Create(projectId2, VersionStamp.Create(), "P2", "P2", LanguageNames.CSharp, - documents: GetDocuments(projectId2, count: 5)), - ProjectInfo.Create(projectId3, VersionStamp.Create(), "P3", "P3", LanguageNames.CSharp, - documents: GetDocuments(projectId3, count: 5)) - }); - - using var workspace = new WorkCoordinatorWorkspace(SolutionCrawlerWorkspaceKind, incrementalAnalyzer: typeof(AnalyzerProvider2)); - await WaitWaiterAsync(workspace.ExportProvider); - - // add analyzer - var lazyWorker = Assert.Single(workspace.ExportProvider.GetExports()); - VerifyMetadata(lazyWorker.Metadata); - var worker = Assert.IsType(Assert.IsAssignableFrom(lazyWorker.Value).Analyzer); - - // enable solution crawler - var service = Assert.IsType(workspace.Services.GetService()); - service.Register(workspace); - - await WaitWaiterAsync(workspace.ExportProvider); - - var listenerProvider = GetListenerProvider(workspace.ExportProvider); - - // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test - var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy).BeginAsyncOperation("Test operation"); - var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawlerLegacy).ExpeditedWaitAsync(); - - // we want to test order items processed by solution crawler. - // but since everything async, lazy and cancellable, order is not 100% deterministic. an item might - // start to be processed, and get cancelled due to newly enqueued item requiring current work to be re-processed - // (ex, new file being added). - // this behavior is expected in real world, but it makes testing hard. so to make ordering deterministic - // here we first block solution crawler from processing any item using global operation. - // and then make sure all delayed work item enqueue to be done through waiters. work item enqueue is async - // and delayed since one of responsibility of solution cralwer is aggregating workspace events to fewer - // work items. - // once we are sure everything is stablized, we let solution crawler to process by releasing global operation. - // what this test is interested in is the order solution crawler process the pending works. so this should - // let the test not care about cancellation or work not enqueued yet. - - // block solution cralwer from processing. - var globalOperation = workspace.Services.SolutionServices.ExportProvider.GetExportedValue(); - using (var operation = globalOperation.Start("Block SolutionCrawler")) - { - // make sure global operaiton is actually started - // otherwise, solution crawler might processed event we are later waiting for - var operationWaiter = GetListenerProvider(workspace.ExportProvider).GetWaiter(FeatureAttribute.GlobalOperation); - await operationWaiter.ExpeditedWaitAsync(); - - // mutate solution - workspace.OnSolutionAdded(solution); - - // wait for workspace events to be all processed - var workspaceWaiter = GetListenerProvider(workspace.ExportProvider).GetWaiter(FeatureAttribute.Workspace); - await workspaceWaiter.ExpeditedWaitAsync(); - - // now wait for semantic processor to finish - var crawlerListener = (AsynchronousOperationListener)GetListenerProvider(workspace.ExportProvider).GetListener(FeatureAttribute.SolutionCrawlerLegacy); - - // first, wait for first work to be queued. - // - // since asyncToken doesn't distinguish whether (1) certain event is happened but all processed or (2) it never happened yet, - // to check (1), we must wait for first item, and then wait for all items to be processed. - await crawlerListener.WaitUntilConditionIsMetAsync( - pendingTokens => pendingTokens.Any(token => token.Tag == (object)SolutionCrawlerRegistrationService.EnqueueItem)); - - // and then wait them to be processed - await crawlerListener.WaitUntilConditionIsMetAsync(pendingTokens => pendingTokens.Where(token => token.Tag == workspace).IsEmpty()); - } - - token.Dispose(); - - // wait analyzers to finish process - await WaitAsync(service, workspace); - await expeditedWait; - - Assert.Equal(1, worker.DocumentIds.Take(5).Select(d => d.ProjectId).Distinct().Count()); - Assert.Equal(1, worker.DocumentIds.Skip(5).Take(5).Select(d => d.ProjectId).Distinct().Count()); - Assert.Equal(1, worker.DocumentIds.Skip(10).Take(5).Select(d => d.ProjectId).Distinct().Count()); - - service.Unregister(workspace); - } - - private static async Task InsertText(string code, string text, bool expectDocumentAnalysis, string language = LanguageNames.CSharp) - { - using var workspace = EditorTestWorkspace.Create( - language, - compilationOptions: null, - parseOptions: null, - [code], - composition: EditorTestCompositions.EditorFeatures.AddExcludedPartTypes(typeof(IIncrementalAnalyzerProvider)).AddParts(typeof(AnalyzerProviderNoWaitNoBlock)), - workspaceKind: SolutionCrawlerWorkspaceKind); - - var testDocument = workspace.Documents.First(); - var textBuffer = testDocument.GetTextBuffer(); - - var lazyWorker = Assert.Single(workspace.ExportProvider.GetExports()); - VerifyMetadata(lazyWorker.Metadata); - var analyzer = Assert.IsType(Assert.IsAssignableFrom(lazyWorker.Value).Analyzer); - Assert.False(analyzer.WaitForCancellation); - Assert.False(analyzer.BlockedRun); - var service = Assert.IsType(workspace.Services.GetService()); - - service.Register(workspace); - - var insertPosition = testDocument.CursorPosition; - - using (var edit = textBuffer.CreateEdit()) - { - edit.Insert(insertPosition.Value, text); - edit.Apply(); - } - - await WaitAsync(service, workspace); - - service.Unregister(workspace); - - Assert.Equal(1, analyzer.SyntaxDocumentIds.Count); - Assert.Equal(expectDocumentAnalysis ? 1 : 0, analyzer.DocumentIds.Count); - } - - private static Task ExecuteOperationAsync(EditorTestWorkspace workspace, Action operation) - => ExecuteOperationAsync(workspace, w => - { - operation(w); - return Task.CompletedTask; - }); - - private static async Task ExecuteOperationAsync(EditorTestWorkspace workspace, Func operation) - { - var lazyWorker = Assert.Single(workspace.ExportProvider.GetExports()); - VerifyMetadata(lazyWorker.Metadata); - - var worker = Assert.IsType(Assert.IsAssignableFrom(lazyWorker.Value).Analyzer); - Assert.False(worker.WaitForCancellation); - Assert.False(worker.BlockedRun); - var service = Assert.IsType(workspace.Services.GetService()); - worker.Reset(workspace); - - service.Register(workspace); - - // don't rely on background parser to have tree. explicitly do it here. - await TouchEverything(workspace.CurrentSolution); - await operation(workspace); - await TouchEverything(workspace.CurrentSolution); - - await WaitAsync(service, workspace); - - service.Unregister(workspace); - - return worker; - } - - private static async Task TouchEverything(Solution solution) - { - foreach (var project in solution.Projects) - { - foreach (var document in project.Documents) - { - await document.GetTextAsync(); - await document.GetSyntaxRootAsync(); - await document.GetSemanticModelAsync(); - } - } - } - - private static async Task WaitAsync(SolutionCrawlerRegistrationService service, EditorTestWorkspace workspace) - { - await WaitWaiterAsync(workspace.ExportProvider); - - service.GetTestAccessor().WaitUntilCompletion(workspace); - } - - private static async Task WaitWaiterAsync(ExportProvider provider) - { - var workspaceWaiter = GetListenerProvider(provider).GetWaiter(FeatureAttribute.Workspace); - await workspaceWaiter.ExpeditedWaitAsync(); - - var solutionCrawlerWaiter = GetListenerProvider(provider).GetWaiter(FeatureAttribute.SolutionCrawlerLegacy); - await solutionCrawlerWaiter.ExpeditedWaitAsync(); - } - - private static SolutionInfo GetInitialSolutionInfoWithP2P() - { - var projectId1 = ProjectId.CreateNewId(); - var projectId2 = ProjectId.CreateNewId(); - var projectId3 = ProjectId.CreateNewId(); - var projectId4 = ProjectId.CreateNewId(); - var projectId5 = ProjectId.CreateNewId(); - - var solution = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(), - projects: new[] - { - ProjectInfo.Create(projectId1, VersionStamp.Create(), "P1", "P1", LanguageNames.CSharp, - documents: new[] { DocumentInfo.Create(DocumentId.CreateNewId(projectId1), "D1") }), - ProjectInfo.Create(projectId2, VersionStamp.Create(), "P2", "P2", LanguageNames.CSharp, - documents: new[] { DocumentInfo.Create(DocumentId.CreateNewId(projectId2), "D2") }, - projectReferences: new[] { new ProjectReference(projectId1) }), - ProjectInfo.Create(projectId3, VersionStamp.Create(), "P3", "P3", LanguageNames.CSharp, - documents: new[] { DocumentInfo.Create(DocumentId.CreateNewId(projectId3), "D3") }, - projectReferences: new[] { new ProjectReference(projectId2) }), - ProjectInfo.Create(projectId4, VersionStamp.Create(), "P4", "P4", LanguageNames.CSharp, - documents: new[] { DocumentInfo.Create(DocumentId.CreateNewId(projectId4), "D4") }), - ProjectInfo.Create(projectId5, VersionStamp.Create(), "P5", "P5", LanguageNames.CSharp, - documents: new[] { DocumentInfo.Create(DocumentId.CreateNewId(projectId5), "D5") }, - projectReferences: new[] { new ProjectReference(projectId4) }), - }); - - return solution; - } - - private static SolutionInfo GetInitialSolutionInfo_2Projects_10Documents() - { - var projectId1 = ProjectId.CreateNewId(); - var projectId2 = ProjectId.CreateNewId(); - - return SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(), - projects: new[] - { - ProjectInfo.Create(projectId1, VersionStamp.Create(), "P1", "P1", LanguageNames.CSharp, - documents: GetDocuments(projectId1, count: 5)), - ProjectInfo.Create(projectId2, VersionStamp.Create(), "P2", "P2", LanguageNames.CSharp, - documents: GetDocuments(projectId2, count: 5)) - }); - } - - private static IEnumerable GetDocuments(ProjectId projectId, int count) - { - for (var i = 0; i < count; i++) - { - yield return DocumentInfo.Create(DocumentId.CreateNewId(projectId), $"D{i + 1}"); - } - } - - private static AsynchronousOperationListenerProvider GetListenerProvider(ExportProvider provider) - => provider.GetExportedValue(); - - private static void MakeFirstDocumentActive(Project project) - => MakeDocumentActive(project.Documents.First()); - - private static void MakeDocumentActive(Document document) - { - var documentTrackingService = (TestDocumentTrackingService)document.Project.Solution.Services.GetRequiredService(); - documentTrackingService.SetActiveDocument(document.Id); - } - - private static void ClearActiveDocument(Workspace workspace) - { - var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetService(); - documentTrackingService.SetActiveDocument(null); - } - - private class WorkCoordinatorWorkspace : EditorTestWorkspace - { - private static readonly TestComposition s_composition = EditorTestCompositions.EditorFeatures - .AddParts(typeof(TestDocumentTrackingService)) - .AddExcludedPartTypes(typeof(IIncrementalAnalyzerProvider)); - - private readonly IAsynchronousOperationWaiter _workspaceWaiter; - private readonly IAsynchronousOperationWaiter _solutionCrawlerWaiter; - - public WorkCoordinatorWorkspace(string workspaceKind = null, bool disablePartialSolutions = true, Type incrementalAnalyzer = null) - : base(composition: incrementalAnalyzer is null ? s_composition : s_composition.AddParts(incrementalAnalyzer), workspaceKind: workspaceKind, disablePartialSolutions: disablePartialSolutions) - { - _workspaceWaiter = GetListenerProvider(ExportProvider).GetWaiter(FeatureAttribute.Workspace); - _solutionCrawlerWaiter = GetListenerProvider(ExportProvider).GetWaiter(FeatureAttribute.SolutionCrawlerLegacy); - - Assert.False(_workspaceWaiter.HasPendingWork); - Assert.False(_solutionCrawlerWaiter.HasPendingWork); - } - - public static WorkCoordinatorWorkspace CreateWithAnalysisScope(BackgroundAnalysisScope analysisScope, string workspaceKind = null, bool disablePartialSolutions = true, Type incrementalAnalyzer = null) - { - var workspace = new WorkCoordinatorWorkspace(workspaceKind, disablePartialSolutions, incrementalAnalyzer); - - var globalOptions = workspace.Services.SolutionServices.ExportProvider.GetExportedValue(); - globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope); - - return workspace; - } - - protected override void Dispose(bool finalize) - { - base.Dispose(finalize); - - Assert.False(_workspaceWaiter.HasPendingWork); - Assert.False(_solutionCrawlerWaiter.HasPendingWork); - } - } - - private class AnalyzerProvider : IIncrementalAnalyzerProvider - { - public readonly IIncrementalAnalyzer Analyzer; - - public AnalyzerProvider(IIncrementalAnalyzer analyzer) - => Analyzer = analyzer; - - public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) - => Analyzer; - } - - [ExportIncrementalAnalyzerProvider(name: "TestAnalyzer", workspaceKinds: new[] { SolutionCrawlerWorkspaceKind })] - [Shared] - [PartNotDiscoverable] - private class AnalyzerProviderNoWaitNoBlock : AnalyzerProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public AnalyzerProviderNoWaitNoBlock(IGlobalOptionService globalOptions) - : base(new Analyzer(globalOptions)) - { - } - } - - [ExportIncrementalAnalyzerProvider(name: "TestAnalyzer", workspaceKinds: new[] { SolutionCrawlerWorkspaceKind })] - [Shared] - [PartNotDiscoverable] - private class AnalyzerProviderWaitNoBlock : AnalyzerProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public AnalyzerProviderWaitNoBlock(IGlobalOptionService globalOptions) - : base(new Analyzer(globalOptions, waitForCancellation: true)) - { - } - } - - [ExportIncrementalAnalyzerProvider(name: "TestAnalyzer", workspaceKinds: new[] { SolutionCrawlerWorkspaceKind })] - [Shared] - [PartNotDiscoverable] - private class AnalyzerProviderNoWaitBlock : AnalyzerProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public AnalyzerProviderNoWaitBlock(IGlobalOptionService globalOptions) - : base(new Analyzer(globalOptions, blockedRun: true)) - { - } - } - - [ExportIncrementalAnalyzerProvider(name: "TestAnalyzer", workspaceKinds: new[] { SolutionCrawlerWorkspaceKind })] - [Shared] - [PartNotDiscoverable] - private class AnalyzerProvider2 : AnalyzerProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public AnalyzerProvider2() - : base(new Analyzer2()) - { - } - } - - internal static void VerifyMetadata(IncrementalAnalyzerProviderMetadata metadata) - { - AssertEx.AreEqual([SolutionCrawlerWorkspaceKind], metadata.WorkspaceKinds); - Assert.False(metadata.HighPriorityForActiveFile); - AssertEx.Equal("TestAnalyzer", metadata.Name); - } - - private class Analyzer : IIncrementalAnalyzer - { - public static readonly Option2 TestOption = new Option2("TestOptions_TestOption", defaultValue: true); - - public readonly ManualResetEventSlim BlockEvent; - public readonly ManualResetEventSlim RunningEvent; - - public readonly HashSet SyntaxDocumentIds = []; - public readonly HashSet DocumentIds = []; - public readonly HashSet NonSourceDocumentIds = []; - public readonly HashSet ProjectIds = []; - - public readonly HashSet InvalidateDocumentIds = []; - public readonly HashSet InvalidateProjectIds = []; - - public readonly HashSet OpenedDocumentIds = []; - public readonly HashSet OpenedNonSourceDocumentIds = []; - public readonly HashSet ClosedDocumentIds = []; - public readonly HashSet ClosedNonSourceDocumentIds = []; - - private readonly IGlobalOptionService _globalOptions; - - private Workspace _workspace; - - public Analyzer(IGlobalOptionService globalOptions, bool waitForCancellation = false, bool blockedRun = false) - { - _globalOptions = globalOptions; - WaitForCancellation = waitForCancellation; - BlockedRun = blockedRun; - - this.BlockEvent = new ManualResetEventSlim(initialState: false); - this.RunningEvent = new ManualResetEventSlim(initialState: false); - - _globalOptions.AddOptionChangedHandler(this, GlobalOptionChanged); - } - - public void Shutdown() - { - _globalOptions.RemoveOptionChangedHandler(this, GlobalOptionChanged); - } - - private void GlobalOptionChanged(object sender, OptionChangedEventArgs e) - { - if (e.Option == TestOption || - e.Option == SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption || - e.Option == SolutionCrawlerOptionsStorage.SolutionBackgroundAnalysisScopeOption) - { - var service = _workspace.Services.GetService(); - service?.Reanalyze(_workspace, this, projectIds: null, documentIds: null, highPriority: false); - } - } - - public bool WaitForCancellation { get; } - - public bool BlockedRun { get; } - - public void Reset(Workspace workspace) - { - _workspace = workspace; - - BlockEvent.Reset(); - RunningEvent.Reset(); - - SyntaxDocumentIds.Clear(); - DocumentIds.Clear(); - NonSourceDocumentIds.Clear(); - ProjectIds.Clear(); - - InvalidateDocumentIds.Clear(); - InvalidateProjectIds.Clear(); - - OpenedDocumentIds.Clear(); - ClosedDocumentIds.Clear(); - OpenedNonSourceDocumentIds.Clear(); - ClosedNonSourceDocumentIds.Clear(); - } - - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) - { - this.ProjectIds.Add(project.Id); - return Task.CompletedTask; - } - - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) - { - if (bodyOpt == null) - { - this.DocumentIds.Add(document.Id); - } - - return Task.CompletedTask; - } - - public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) - { - this.SyntaxDocumentIds.Add(document.Id); - Process(document.Id, cancellationToken); - return Task.CompletedTask; - } - - public Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) - { - this.NonSourceDocumentIds.Add(textDocument.Id); - return Task.CompletedTask; - } - - public async Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) - { - if (_globalOptions.GetBackgroundAnalysisScope(document.Project.Language) != BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics) - { - return; - } - - if (document is Document sourceDocument) - { - await AnalyzeSyntaxAsync(sourceDocument, InvocationReasons.ActiveDocumentSwitched, cancellationToken).ConfigureAwait(false); - await AnalyzeDocumentAsync(sourceDocument, bodyOpt: null, InvocationReasons.ActiveDocumentSwitched, cancellationToken).ConfigureAwait(false); - } - else - { - await AnalyzeNonSourceDocumentAsync(document, InvocationReasons.ActiveDocumentSwitched, cancellationToken).ConfigureAwait(false); - } - } - - public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) - { - InvalidateDocumentIds.Add(documentId); - return Task.CompletedTask; - } - - public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) - { - InvalidateProjectIds.Add(projectId); - return Task.CompletedTask; - } - - public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) - { - OpenedDocumentIds.Add(document.Id); - return Task.CompletedTask; - } - - public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) - { - ClosedDocumentIds.Add(document.Id); - return Task.CompletedTask; - } - - public Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken) - { - OpenedNonSourceDocumentIds.Add(textDocument.Id); - return Task.CompletedTask; - } - - public Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken) - { - ClosedNonSourceDocumentIds.Add(textDocument.Id); - return Task.CompletedTask; - } - - private void Process(DocumentId _, CancellationToken cancellationToken) - { - if (BlockedRun && !RunningEvent.IsSet) - { - this.RunningEvent.Set(); - - // Wait until unblocked - this.BlockEvent.Wait(); - } - - if (WaitForCancellation && !RunningEvent.IsSet) - { - this.RunningEvent.Set(); - - cancellationToken.WaitHandle.WaitOne(); - cancellationToken.ThrowIfCancellationRequested(); - } - } - - #region unused - public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) - => Task.CompletedTask; - - public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) - => Task.CompletedTask; - - public Task NonSourceDocumentResetAsync(TextDocument textDocument, CancellationToken cancellationToken) - => Task.CompletedTask; - - public void LogAnalyzerCountSummary() - { - } - - public int Priority => 1; - - #endregion - } - - private class Analyzer2 : IIncrementalAnalyzer - { - public readonly List DocumentIds = []; - - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) - { - this.DocumentIds.Add(document.Id); - return Task.CompletedTask; - } - - #region unused - public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) => Task.CompletedTask; - public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) => Task.CompletedTask; - public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) => Task.CompletedTask; - public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) => Task.CompletedTask; - public Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) => Task.CompletedTask; - public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) => Task.CompletedTask; - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) => Task.CompletedTask; - public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) => Task.CompletedTask; - public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) => Task.CompletedTask; - public Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken) => Task.CompletedTask; - public Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken) => Task.CompletedTask; - public Task NonSourceDocumentResetAsync(TextDocument textDocument, CancellationToken cancellationToken) => Task.CompletedTask; - public Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) => Task.CompletedTask; - - public void LogAnalyzerCountSummary() - { - } - - public int Priority => 1; - - public void Shutdown() - { - } - - #endregion - } - -#if false - private string GetListenerTrace(ExportProvider provider) - { - var sb = new StringBuilder(); - - var workspaceWaiter = GetListeners(provider).First(l => l.Metadata.FeatureName == FeatureAttribute.Workspace).Value as TestAsynchronousOperationListener; - sb.AppendLine("workspace"); - sb.AppendLine(workspaceWaiter.Trace()); - - var solutionCrawlerWaiter = GetListeners(provider).First(l => l.Metadata.FeatureName == FeatureAttribute.SolutionCrawlerLegacy).Value as TestAsynchronousOperationListener; - sb.AppendLine("solutionCrawler"); - sb.AppendLine(solutionCrawlerWaiter.Trace()); - - return sb.ToString(); - } - - internal abstract partial class TestAsynchronousOperationListener : IAsynchronousOperationListener, IAsynchronousOperationWaiter - { - private readonly object gate = new object(); - private readonly HashSet> pendingTasks = new HashSet>(); - private readonly StringBuilder sb = new StringBuilder(); - - private int counter; - - public TestAsynchronousOperationListener() - { - } - - public IAsyncToken BeginAsyncOperation(string name, object tag = null) - { - lock (gate) - { - return new AsyncToken(this, name); - } - } - - private void Increment(string name) - { - lock (gate) - { - sb.AppendLine("i -> " + name + ":" + counter++); - } - } - - private void Decrement(string name) - { - lock (gate) - { - counter--; - if (counter == 0) - { - foreach (var task in pendingTasks) - { - task.SetResult(true); - } - - pendingTasks.Clear(); - } - - sb.AppendLine("d -> " + name + ":" + counter); - } - } - - public virtual Task CreateWaitTask() - { - lock (gate) - { - var source = new TaskCompletionSource(); - if (counter == 0) - { - // There is nothing to wait for, so we are immediately done - source.SetResult(true); - } - else - { - pendingTasks.Add(source); - } - - return source.Task; - } - } - - public bool TrackActiveTokens { get; set; } - - public bool HasPendingWork - { - get - { - return counter != 0; - } - } - - private class AsyncToken : IAsyncToken - { - private readonly TestAsynchronousOperationListener listener; - private readonly string name; - private bool disposed; - - public AsyncToken(TestAsynchronousOperationListener listener, string name) - { - this.listener = listener; - this.name = name; - - listener.Increment(name); - } - - public void Dispose() - { - lock (listener.gate) - { - if (disposed) - { - throw new InvalidOperationException("Double disposing of an async token"); - } - - disposed = true; - listener.Decrement(this.name); - } - } - } - - public string Trace() - { - return sb.ToString(); - } - } -#endif - } -} -#endif From bcf7ade835e1272dfa06bd080d2303c0ae85cda3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:51:21 -0800 Subject: [PATCH 066/151] simplify --- .../Test2/Diagnostics/DiagnosticProviderTests.vb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index 4d8d6439705af..bfc4c8a47c2df 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -268,11 +268,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, language, BackgroundAnalysisScope.OpenFiles) Next -#If False Then - Dim registrationService = workspace.Services.GetService(Of ISolutionCrawlerRegistrationService)() - registrationService.Register(workspace) -#End If - Dim diagnosticProvider = GetDiagnosticProvider(workspace) Dim actualDiagnostics = diagnosticProvider.GetCachedDiagnosticsAsync(workspace, projectId:=Nothing, documentId:=Nothing, includeSuppressedDiagnostics:=False, @@ -280,10 +275,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests includeNonLocalDocumentDiagnostics:=True, CancellationToken.None).Result -#If False Then - registrationService.Unregister(workspace) -#End If - If diagnostics Is Nothing Then Assert.Equal(0, actualDiagnostics.Length) Else @@ -308,12 +299,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim analyzerService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) - ' CollectErrors generates interleaved background and foreground tasks. -#If False Then - Dim service = DirectCast(workspace.Services.GetService(Of ISolutionCrawlerRegistrationService)(), SolutionCrawlerRegistrationService) - service.GetTestAccessor().WaitUntilCompletion(workspace, SpecializedCollections.SingletonEnumerable(analyzerService.CreateIncrementalAnalyzer(workspace)).WhereNotNull().ToImmutableArray()) -#End If - Return analyzerService End Function From dc64a319fd5a4a139c5df2db1f8bdfe39a9d0ffd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:51:46 -0800 Subject: [PATCH 067/151] simplify --- .../Diagnostics/DiagnosticServiceTests.vb | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 6bdf2db136ce1..13b9317320c04 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2198,10 +2198,6 @@ class MyClass Assert.Equal(1, descriptors.Length) Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id) -#If False Then - ' Force project analysis - incrementalAnalyzer.AnalyzeProjectAsync(project, semanticsChanged:=True, reasons:=InvocationReasons.Empty, cancellationToken:=CancellationToken.None).Wait() -#End If ' Get cached project diagnostics. Dim diagnostics = Await diagnosticService.GetCachedDiagnosticsAsync(workspace, project.Id, documentId:=Nothing, @@ -2504,25 +2500,6 @@ class MyClass End Using End Function -#If False Then - - Public Sub ReanalysisScopeExcludesMissingDocuments() - Dim test = - - - - - Using workspace = TestWorkspace.CreateWorkspace(test, composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) - Dim solution = workspace.CurrentSolution - Dim project = solution.Projects.Single() - - Dim missingDocumentId = DocumentId.CreateNewId(project.Id, "Missing ID") - Dim reanalysisScope = New SolutionCrawlerRegistrationService.ReanalyzeScope(documentIds:={missingDocumentId}) - Assert.Empty(reanalysisScope.GetDocumentIds(solution)) - End Using - End Sub -#End If - Private NotInheritable Class AnalyzerWithCustomDiagnosticCategory Inherits DiagnosticAnalyzer From 60e8c2598d04da9c2c77333336f306e144a34b6d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:55:02 -0800 Subject: [PATCH 068/151] simplify --- .../Diagnostics/DiagnosticTaggerWrapper.cs | 21 +------------------ .../Squiggles/SquiggleUtilities.cs | 2 +- .../Squiggles/TestDiagnosticTagProducer.cs | 2 +- .../Host/RemoteDocumentDifferenceService.cs | 4 ++-- 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index b66dbf8438c27..781ade8d6577d 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -19,15 +19,12 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics { - internal class DiagnosticTaggerWrapper : IDisposable + internal class DiagnosticTaggerWrapper where TProvider : AbstractDiagnosticsTaggerProvider where TTag : ITag { private readonly EditorTestWorkspace _workspace; public readonly DiagnosticAnalyzerService? AnalyzerService; -#if false - private readonly SolutionCrawlerRegistrationService _registrationService; -#endif public readonly DiagnosticService DiagnosticService; private readonly IThreadingContext _threadingContext; private readonly IAsynchronousOperationListenerProvider _listenerProvider; @@ -53,15 +50,6 @@ public DiagnosticTaggerWrapper( _workspace = workspace; -#if false - _registrationService = (SolutionCrawlerRegistrationService)workspace.Services.GetRequiredService(); - _registrationService.Register(workspace); - - if (!_registrationService.GetTestAccessor().TryGetWorkCoordinator(workspace, out var coordinator)) - throw new InvalidOperationException(); - - AnalyzerService = (DiagnosticAnalyzerService?)_registrationService.GetTestAccessor().AnalyzerProviders.SelectMany(pair => pair.Value).SingleOrDefault(lazyProvider => lazyProvider.Metadata.Name == WellKnownSolutionCrawlerAnalyzers.Diagnostic && lazyProvider.Metadata.HighPriorityForActiveFile)?.Value; -#endif DiagnosticService = (DiagnosticService)workspace.ExportProvider.GetExportedValue(); if (updateSource is object) @@ -99,13 +87,6 @@ public ITaggerProvider TaggerProvider } } - public void Dispose() - { -#if false - _registrationService.Unregister(_workspace); -#endif - } - public async Task WaitForTags() { await _listenerProvider.WaitAllDispatcherOperationAndTasksAsync( diff --git a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs index 30a907a5e0930..efba61a1b8d2f 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs @@ -34,7 +34,7 @@ public static class SquiggleUtilities where TProvider : AbstractDiagnosticsAdornmentTaggerProvider where TTag : class, ITag { - using var wrapper = new DiagnosticTaggerWrapper(workspace, analyzerMap); + var wrapper = new DiagnosticTaggerWrapper(workspace, analyzerMap); var firstDocument = workspace.Documents.First(); var textBuffer = firstDocument.GetTextBuffer(); diff --git a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs index c78608a989456..6e5fff0f93733 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs @@ -34,7 +34,7 @@ internal static async Task>> GetErrorsFromUpdateSource(Edit { var source = new TestDiagnosticUpdateSource(); - using var wrapper = new DiagnosticTaggerWrapper(workspace, updateSource: source); + var wrapper = new DiagnosticTaggerWrapper(workspace, updateSource: source); var firstDocument = workspace.Documents.First(); var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs index ebb70b99b1352..0a19d7e3d626f 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs @@ -43,9 +43,9 @@ public VisualBasicDocumentDifferenceService() } } - public async Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) + public Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { - return null; + return SpecializedTasks.Null(); #if false // in remote workspace, we don't trust any version based on VersionStamp. we only trust content based information such as // checksum or tree comparison and etc. From d11dad9e96468ecc089000ef01d18ae641d4118f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:55:31 -0800 Subject: [PATCH 069/151] simplify --- .../AbstractLanguageServerProtocolTests.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 6b8982d5b0212..df7b41725e8af 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -397,12 +397,6 @@ internal EditorTestWorkspace CreateWorkspace( workspace.GetService().Register(workspace); -#if false - // solution crawler is currently required in order to create incremental analyzer that provides diagnostics - var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)workspace.Services.GetRequiredService(); - solutionCrawlerRegistrationService.Register(workspace); -#endif - return workspace; } @@ -745,11 +739,6 @@ public async ValueTask DisposeAsync() TestWorkspace.GetService().Deregister(TestWorkspace); TestWorkspace.GetService().Deregister(GetManagerAccessor().GetLspMiscellaneousFilesWorkspace()); -#if false - var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)TestWorkspace.Services.GetRequiredService(); - solutionCrawlerRegistrationService.Unregister(TestWorkspace); -#endif - // Some tests will manually call shutdown and exit, so attempting to call this during dispose // will fail as the server's jsonrpc instance will be disposed of. if (!LanguageServer.GetTestAccessor().HasShutdownStarted()) From 0cd3a33ee7c870fbc7a0854c98122a9e159cd4c6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:55:51 -0800 Subject: [PATCH 070/151] simplify --- .../Portable/SolutionCrawler/Extensions.cs | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/Extensions.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/Extensions.cs b/src/Features/Core/Portable/SolutionCrawler/Extensions.cs deleted file mode 100644 index 33681a7d3815f..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/Extensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System; -using System.Text; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal static class Extensions -{ - public static string ToBase64(this string data) - { - // Write out the message in base64, since it may contain - // user code that can't be represented in xml. (see - // http://vstfdevdiv:8080/web/wi.aspx?pcguid=22f9acc9-569a-41ff-b6ac-fac1b6370209&id=578059) - return Convert.ToBase64String(Encoding.UTF8.GetBytes(data)); - } - - public static string DecodeBase64(this string data) - { - var bytes = Convert.FromBase64String(data); - return Encoding.UTF8.GetString(bytes, 0, bytes.Length); - } -} -#endif From 8529020d9e74521cd8f3d7bae4567899d7078e38 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:56:06 -0800 Subject: [PATCH 071/151] simplify --- .../SolutionCrawler/IDocumentDifferenceService.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs b/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs index 9f1e0a9e45c5b..ea31aac3136eb 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IDocumentDifferenceService.cs @@ -8,18 +8,7 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler; -#if false -internal class DocumentDifferenceResult(InvocationReasons changeType, SyntaxNode? changedMember = null) -{ - public InvocationReasons ChangeType { get; } = changeType; - public SyntaxNode? ChangedMember { get; } = changedMember; -} -#endif - internal interface IDocumentDifferenceService : ILanguageService { -#if false - Task GetDifferenceAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); -#endif Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); } From 8bb172792761c89d7d68df330e60ebf6eb73eca9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:56:18 -0800 Subject: [PATCH 072/151] simplify --- .../ISolutionCrawlerProgressReporter.cs | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs b/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs deleted file mode 100644 index 0bc8bdaff8847..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerProgressReporter.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -/// -/// Provide a way to see whether solution crawler is started or not -/// -internal interface ISolutionCrawlerProgressReporter -{ - /// - /// Return true if solution crawler is in progress. - /// - bool InProgress { get; } - - /// - /// Raised when solution crawler progress changed - /// - /// Notifications for this event are serialized to preserve order. - /// However, individual event notifications may occur on any thread. - /// - event EventHandler ProgressChanged; -} - -internal readonly struct ProgressData(ProgressStatus type, int? pendingItemCount) -{ - public ProgressStatus Status { get; } = type; - - /// - /// number of pending work item in the queue. - /// null means N/A for the associated - /// - public int? PendingItemCount { get; } = pendingItemCount; -} - -internal enum ProgressStatus -{ - Started, - Paused, - PendingItemCountUpdated, - Evaluating, - Stopped -} -#endif From ffce41b2904684dc46f6d7f0269e95953129197c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:56:26 -0800 Subject: [PATCH 073/151] simplify --- .../ISolutionCrawlerService.cs | 29 ------------------- 1 file changed, 29 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs b/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs deleted file mode 100644 index 7ce54ca970f52..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System.Collections.Generic; -using Microsoft.CodeAnalysis.Host; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -/// -/// Provide a way to control solution crawler. -/// -internal interface ISolutionCrawlerService : IWorkspaceService -{ - /// - /// Ask solution crawler to re-analyze given s or/and s - /// in given with given . - /// If both and are null, the entire - /// for the given is re-analyzed. - /// - void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority); - - /// - /// Get for the given - /// - ISolutionCrawlerProgressReporter GetProgressReporter(Workspace workspace); -} -#endif From ee46be3ca7a352de679e0294c05c373ac53d6c20 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:56:34 -0800 Subject: [PATCH 074/151] simplify --- .../IWorkCoordinatorPriorityService.cs | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs b/src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs deleted file mode 100644 index 98fada0d2075b..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/IWorkCoordinatorPriorityService.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal interface IWorkCoordinatorPriorityService : ILanguageService -{ - /// - /// True if this document is less important than other documents in the project it is - /// contained in, and should have work scheduled for it happen after all other documents - /// in the project. - /// - Task IsLowPriorityAsync(Document document, CancellationToken cancellationToken); -} -#endif From 8406b4082c5220e8e0ca0aa282cd66d65ad5a43b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:56:42 -0800 Subject: [PATCH 075/151] simplify --- .../IncrementalAnalyzerBase.cs | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs b/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs deleted file mode 100644 index e27cc43ac0d21..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/IncrementalAnalyzerBase.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal class IncrementalAnalyzerBase : IIncrementalAnalyzer -{ - protected IncrementalAnalyzerBase() - { - } - - public virtual Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task DocumentResetAsync(Document document, CancellationToken cancellationToken) - => Task.CompletedTask; - - public Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellation) - => Task.CompletedTask; - - public virtual Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task NonSourceDocumentResetAsync(TextDocument textDocument, CancellationToken cancellationToken) - => Task.CompletedTask; - - public virtual Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) - => Task.CompletedTask; - - public void LogAnalyzerCountSummary() - { - } - - /// - /// Order all incremental analyzers below DiagnosticIncrementalAnalyzer - /// - public virtual int Priority => 1; - - public virtual void Shutdown() - { - } -} -#endif From 83db39710f02bb2d3d670230b741afadce67b8c4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:56:49 -0800 Subject: [PATCH 076/151] simplify --- .../SolutionCrawler/SolutionCrawlerLogger.cs | 297 ------------------ 1 file changed, 297 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs deleted file mode 100644 index e15d83827c38f..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerLogger.cs +++ /dev/null @@ -1,297 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Internal.Log; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal static class SolutionCrawlerLogger -{ - private const string Id = nameof(Id); - private const string Kind = nameof(Kind); - private const string Analyzer = nameof(Analyzer); - private const string DocumentCount = nameof(DocumentCount); - private const string Languages = nameof(Languages); - private const string HighPriority = nameof(HighPriority); - private const string Enabled = nameof(Enabled); - private const string AnalyzerCount = nameof(AnalyzerCount); - private const string PersistentStorage = nameof(PersistentStorage); - private const string GlobalOperation = nameof(GlobalOperation); - private const string HigherPriority = nameof(HigherPriority); - private const string LowerPriority = nameof(LowerPriority); - private const string TopLevel = nameof(TopLevel); - private const string MemberLevel = nameof(MemberLevel); - private const string NewWorkItem = nameof(NewWorkItem); - private const string UpdateWorkItem = nameof(UpdateWorkItem); - private const string ProjectEnqueue = nameof(ProjectEnqueue); - private const string ResetStates = nameof(ResetStates); - private const string ProjectNotExist = nameof(ProjectNotExist); - private const string DocumentNotExist = nameof(DocumentNotExist); - private const string ProcessProject = nameof(ProcessProject); - private const string OpenDocument = nameof(OpenDocument); - private const string CloseDocument = nameof(CloseDocument); - private const string SolutionHash = nameof(SolutionHash); - private const string ProcessDocument = nameof(ProcessDocument); - private const string ProcessDocumentCancellation = nameof(ProcessDocumentCancellation); - private const string ProcessProjectCancellation = nameof(ProcessProjectCancellation); - private const string ActiveFileEnqueue = nameof(ActiveFileEnqueue); - private const string ActiveFileProcessDocument = nameof(ActiveFileProcessDocument); - private const string ActiveFileProcessDocumentCancellation = nameof(ActiveFileProcessDocumentCancellation); - - public static void LogRegistration(int correlationId, Workspace workspace) - { - Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Register, KeyValueLogMessage.Create(m => - { - m[Id] = correlationId; - m[Kind] = workspace.Kind; - })); - } - - public static void LogUnregistration(int correlationId) - { - Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Unregister, KeyValueLogMessage.Create(m => - { - m[Id] = correlationId; - })); - } - - public static void LogReanalyze( - int correlationId, - IIncrementalAnalyzer analyzer, - int documentCount, - string languages, - bool highPriority) - { - Logger.Log(FunctionId.WorkCoordinatorRegistrationService_Reanalyze, KeyValueLogMessage.Create(m => - { - m[Id] = correlationId; - m[Analyzer] = analyzer.ToString(); - m[DocumentCount] = documentCount; - m[HighPriority] = highPriority; - m[Languages] = languages; - })); - } - - public static void LogAnalyzers(int correlationId, Workspace workspace, ImmutableArray reordered, bool onlyHighPriorityAnalyzer) - { - if (onlyHighPriorityAnalyzer) - { - LogAnalyzersWorker( - FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzers, FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzer, - correlationId, workspace, reordered); - } - else - { - LogAnalyzersWorker( - FunctionId.IncrementalAnalyzerProcessor_Analyzers, FunctionId.IncrementalAnalyzerProcessor_Analyzer, - correlationId, workspace, reordered); - } - } - - private static void LogAnalyzersWorker( - FunctionId analyzersId, FunctionId analyzerId, int correlationId, Workspace workspace, ImmutableArray reordered) - { - if (workspace.Kind == WorkspaceKind.Preview) - { - return; - } - - // log registered analyzers. - Logger.Log(analyzersId, KeyValueLogMessage.Create(m => - { - m[Id] = correlationId; - m[AnalyzerCount] = reordered.Length; - }, LogLevel.Debug)); - - foreach (var analyzer in reordered) - { - Logger.Log(analyzerId, KeyValueLogMessage.Create(m => - { - m[Id] = correlationId; - m[Analyzer] = analyzer.ToString(); - }, LogLevel.Debug)); - } - } - - public static void LogWorkCoordinatorShutdownTimeout(int correlationId) - { - Logger.Log(FunctionId.WorkCoordinator_ShutdownTimeout, KeyValueLogMessage.Create(m => - { - m[Id] = correlationId; - })); - } - - public static void LogWorkspaceEvent(CountLogAggregator logAggregator, WorkspaceChangeKind kind) - => logAggregator.IncreaseCount(kind); - - public static void LogWorkCoordinatorShutdown(int correlationId, CountLogAggregator logAggregator) - { - Logger.Log(FunctionId.WorkCoordinator_Shutdown, KeyValueLogMessage.Create(m => - { - m[Id] = correlationId; - - foreach (var kv in logAggregator) - { - var change = kv.Key.ToString(); - m[change] = kv.Value.GetCount(); - } - })); - } - - public static void LogGlobalOperation(CountLogAggregator logAggregator) - => logAggregator.IncreaseCount(GlobalOperation); - - public static void LogActiveFileEnqueue(CountLogAggregator logAggregator) - => logAggregator.IncreaseCount(ActiveFileEnqueue); - - public static void LogWorkItemEnqueue(CountLogAggregator logAggregator, ProjectId _) - => logAggregator.IncreaseCount(ProjectEnqueue); - - public static void LogWorkItemEnqueue( - CountLogAggregator logAggregator, string language, DocumentId? documentId, InvocationReasons reasons, bool lowPriority, SyntaxPath? activeMember, bool added) - { - logAggregator.IncreaseCount(language); - logAggregator.IncreaseCount(added ? NewWorkItem : UpdateWorkItem); - - if (documentId != null) - { - logAggregator.IncreaseCount(activeMember == null ? TopLevel : MemberLevel); - - if (lowPriority) - { - logAggregator.IncreaseCount(LowerPriority); - logAggregator.IncreaseCount(ValueTuple.Create(LowerPriority, documentId.Id)); - } - } - - foreach (var reason in reasons) - { - logAggregator.IncreaseCount(reason); - } - } - - public static void LogHigherPriority(CountLogAggregator logAggregator, Guid documentId) - { - logAggregator.IncreaseCount(HigherPriority); - logAggregator.IncreaseCount(ValueTuple.Create(HigherPriority, documentId)); - } - - public static void LogResetStates(CountLogAggregator logAggregator) - => logAggregator.IncreaseCount(ResetStates); - - public static void LogIncrementalAnalyzerProcessorStatistics(int correlationId, Solution solution, CountLogAggregator logAggregator, ImmutableArray analyzers) - { - Logger.Log(FunctionId.IncrementalAnalyzerProcessor_Shutdown, KeyValueLogMessage.Create(m => - { - var solutionHash = GetSolutionHash(solution); - - m[Id] = correlationId; - m[SolutionHash] = solutionHash.ToString(); - - var statMap = new Dictionary>(); - foreach (var (key, counter) in logAggregator) - { - if (key is string stringKey) - { - m[stringKey] = counter.GetCount(); - } - else if (key is ValueTuple propertyNameAndId) - { - var list = statMap.GetOrAdd(propertyNameAndId.Item1, _ => []); - list.Add(counter.GetCount()); - } - else - { - throw ExceptionUtilities.Unreachable(); - } - } - - foreach (var (propertyName, propertyValues) in statMap) - { - var result = StatisticResult.FromList(propertyValues); - - result.WriteTelemetryPropertiesTo(m, prefix: propertyName); - } - })); - - foreach (var analyzer in analyzers) - { - analyzer.LogAnalyzerCountSummary(); - } - } - - private static int GetSolutionHash(Solution solution) - { - if (solution != null && solution.FilePath != null) - { - return solution.FilePath.ToLowerInvariant().GetHashCode(); - } - - return 0; - } - - public static void LogProcessCloseDocument(CountLogAggregator logAggregator, Guid documentId) - { - logAggregator.IncreaseCount(CloseDocument); - logAggregator.IncreaseCount(ValueTuple.Create(CloseDocument, documentId)); - } - - public static void LogProcessOpenDocument(CountLogAggregator logAggregator, Guid documentId) - { - logAggregator.IncreaseCount(OpenDocument); - logAggregator.IncreaseCount(ValueTuple.Create(OpenDocument, documentId)); - } - - public static void LogProcessActiveFileDocument(CountLogAggregator logAggregator, Guid _, bool processed) - { - if (processed) - { - logAggregator.IncreaseCount(ActiveFileProcessDocument); - } - else - { - logAggregator.IncreaseCount(ActiveFileProcessDocumentCancellation); - } - } - - public static void LogProcessDocument(CountLogAggregator logAggregator, Guid documentId, bool processed) - { - if (processed) - { - logAggregator.IncreaseCount(ProcessDocument); - } - else - { - logAggregator.IncreaseCount(ProcessDocumentCancellation); - } - - logAggregator.IncreaseCount(ValueTuple.Create(ProcessDocument, documentId)); - } - - public static void LogProcessDocumentNotExist(CountLogAggregator logAggregator) - => logAggregator.IncreaseCount(DocumentNotExist); - - public static void LogProcessProject(CountLogAggregator logAggregator, Guid projectId, bool processed) - { - if (processed) - { - logAggregator.IncreaseCount(ProcessProject); - } - else - { - logAggregator.IncreaseCount(ProcessProjectCancellation); - } - - logAggregator.IncreaseCount(ValueTuple.Create(ProcessProject, projectId)); - } - - public static void LogProcessProjectNotExist(CountLogAggregator logAggregator) - => logAggregator.IncreaseCount(ProjectNotExist); -} -#endif From 271dfe6745ca63fb34dbd8f8152d1f1cef09badb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:56:58 -0800 Subject: [PATCH 077/151] simplify --- .../SolutionCrawlerProgressReporter.cs | 112 ------------------ 1 file changed, 112 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs deleted file mode 100644 index e587d74263951..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerProgressReporter.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Threading; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService : ISolutionCrawlerRegistrationService -{ - /// - /// Progress reporter - /// - /// this progress reporter is a best effort implementation. it doesn't stop world to find out accurate data - /// - /// what this reporter care is we show start/stop background work and show things are moving or paused - /// without too much cost. - /// - /// due to how solution cralwer calls Start/Stop (see caller of those 2), those 2 can't have a race - /// and that is all we care for this reporter - /// - internal sealed class SolutionCrawlerProgressReporter : ISolutionCrawlerProgressReporter - { - // we use ref count here since solution crawler has multiple queues per priority - // where an item can be enqueued and dequeued independently. - // first item added in any of those queues will cause the "start" event to be sent - // and the very last item processed from those queues will cause "stop" event to be sent - // evaluating and paused is also ref counted since work in the lower priority queue can - // be canceled due to new higher priority work item enqueued to higher queue. - // but before lower priority work actually exit due to cancellation, higher work could - // start processing. causing an overlap. the ref count make sure that exiting lower - // work doesn't flip evaluating state to paused state. - private int _progressStartCount = 0; - private int _progressEvaluateCount = 0; - - public event EventHandler? ProgressChanged; - - public bool InProgress => _progressStartCount > 0; - - public void Start() => ChangeProgressStatus(ref _progressStartCount, ProgressStatus.Started); - public void Stop() => ChangeProgressStatus(ref _progressStartCount, ProgressStatus.Stopped); - - private void Evaluate() => ChangeProgressStatus(ref _progressEvaluateCount, ProgressStatus.Evaluating); - private void Pause() => ChangeProgressStatus(ref _progressEvaluateCount, ProgressStatus.Paused); - - public void UpdatePendingItemCount(int pendingItemCount) - { - if (_progressStartCount > 0) - { - var progressData = new ProgressData(ProgressStatus.PendingItemCountUpdated, pendingItemCount); - OnProgressChanged(progressData); - } - } - - /// - /// Allows the solution crawler to start evaluating work enqueued to it. - /// Returns an IDisposable that the caller must dispose of to indicate that it no longer needs the crawler to continue evaluating. - /// Multiple callers can call into this simultaneously. - /// Only when the last one actually disposes the scope-object will the crawler - /// actually revert back to the paused state where no work proceeds. - /// - public IDisposable GetEvaluatingScope() - => new ProgressStatusRAII(this); - - private void ChangeProgressStatus(ref int referenceCount, ProgressStatus status) - { - var start = status is ProgressStatus.Started or ProgressStatus.Evaluating; - if (start ? (Interlocked.Increment(ref referenceCount) == 1) : (Interlocked.Decrement(ref referenceCount) == 0)) - { - var progressData = new ProgressData(status, pendingItemCount: null); - OnProgressChanged(progressData); - } - } - - private void OnProgressChanged(ProgressData progressData) - => ProgressChanged?.Invoke(this, progressData); - - private readonly struct ProgressStatusRAII : IDisposable - { - private readonly SolutionCrawlerProgressReporter _owner; - - public ProgressStatusRAII(SolutionCrawlerProgressReporter owner) - { - _owner = owner; - _owner.Evaluate(); - } - - public void Dispose() - => _owner.Pause(); - } - } - - /// - /// reporter that doesn't do anything - /// - private class NullReporter : ISolutionCrawlerProgressReporter - { - public static readonly NullReporter Instance = new(); - - public bool InProgress => false; - - public event EventHandler ProgressChanged - { - add { } - remove { } - } - } -} -#endif From 3736dbd86a1fcb3e86cbb0bcc3bdd2a9246ae0fb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:07 -0800 Subject: [PATCH 078/151] simplify --- .../SolutionCrawlerRegistrationService.cs | 320 ------------------ 1 file changed, 320 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs deleted file mode 100644 index 6795053900967..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerRegistrationService.cs +++ /dev/null @@ -1,320 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Composition; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; -using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer; - -namespace Microsoft.CodeAnalysis.SolutionCrawler -{ - [ExportWorkspaceService(typeof(ISolutionCrawlerRegistrationService), ServiceLayer.Host), Shared] - internal partial class SolutionCrawlerRegistrationService : ISolutionCrawlerRegistrationService - { - private const string Default = "*"; - - private readonly object _gate = new(); - private readonly SolutionCrawlerProgressReporter _progressReporter = new(); - - private readonly IAsynchronousOperationListener _listener; - private readonly Dictionary _documentWorkCoordinatorMap; - - private ImmutableDictionary>> _analyzerProviders; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SolutionCrawlerRegistrationService( - [ImportMany] IEnumerable> analyzerProviders, - IAsynchronousOperationListenerProvider listenerProvider) - { - _analyzerProviders = analyzerProviders.GroupBy(kv => kv.Metadata.Name).ToImmutableDictionary(g => g.Key, g => g.ToImmutableArray()); - AssertAnalyzerProviders(_analyzerProviders); - - _documentWorkCoordinatorMap = new Dictionary(ReferenceEqualityComparer.Instance); - _listener = listenerProvider.GetListener(FeatureAttribute.SolutionCrawlerLegacy); - } - - void ISolutionCrawlerRegistrationService.Register(Workspace workspace) - => Register(workspace); - - public bool Register(Workspace workspace) - { - // Do not crawl the preview workspace. It's pure overhead and serves no purpose. Diagnostics for the - // preview workspace are provided either through Roslyn-Native-Pull-Tagging (which does not need solution - // crawler). Or will be something LSP needs to handle if Native-Pull-Tagging is off and - // LSP-Pull-Diagnostics is on. - if (workspace.Kind == WorkspaceKind.Preview) - return false; - - EnsureRegistration(workspace, initializeLazily: true); - return true; - } - - /// - /// make sure solution cralwer is registered for the given workspace. - /// - /// this solution crawler runs for - /// - /// when true, solution crawler will be initialized when there is the first workspace event fired. - /// otherwise, it will be initialized when workspace is registered right away. - /// something like "Build" will use initializeLazily:false to make sure diagnostic analyzer engine (incremental analyzer) - /// is initialized. otherwise, if build is called before workspace is fully populated, we will think some errors from build - /// doesn't belong to us since diagnostic analyzer engine is not there yet and - /// let project system to take care of these unknown errors. - /// - public void EnsureRegistration(Workspace workspace, bool initializeLazily) - { - Contract.ThrowIfNull(workspace.Kind); - - var correlationId = CorrelationIdFactory.GetNextId(); - - lock (_gate) - { - if (_documentWorkCoordinatorMap.ContainsKey(workspace)) - { - // already registered. - return; - } - - var coordinator = new WorkCoordinator( - _listener, - GetAnalyzerProviders(workspace.Kind), - initializeLazily, - new Registration(correlationId, workspace, _progressReporter)); - - _documentWorkCoordinatorMap.Add(workspace, coordinator); - } - - SolutionCrawlerLogger.LogRegistration(correlationId, workspace); - } - - public void Unregister(Workspace workspace, bool blockingShutdown = false) - { - WorkCoordinator? coordinator; - - lock (_gate) - { - if (!_documentWorkCoordinatorMap.TryGetValue(workspace, out coordinator)) - { - // already unregistered - return; - } - - _documentWorkCoordinatorMap.Remove(workspace); - coordinator.Shutdown(blockingShutdown); - } - - SolutionCrawlerLogger.LogUnregistration(coordinator.CorrelationId); - } - - public void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata) - { - // now update all existing work coordinator - lock (_gate) - { - var lazyProvider = new Lazy(() => provider, metadata); - - // update existing map for future solution crawler registration - no need for interlock but this makes add or update easier - ImmutableInterlocked.AddOrUpdate(ref _analyzerProviders, metadata.Name, n => [lazyProvider], (n, v) => v.Add(lazyProvider)); - - // assert map integrity - AssertAnalyzerProviders(_analyzerProviders); - - // find existing coordinator to update - var lazyProviders = _analyzerProviders[metadata.Name]; - foreach (var (workspace, coordinator) in _documentWorkCoordinatorMap) - { - Contract.ThrowIfNull(workspace.Kind); - - if (!TryGetProvider(workspace.Kind, lazyProviders, out var picked) || picked != lazyProvider) - { - // check whether new provider belong to current workspace - continue; - } - - var analyzer = lazyProvider.Value.CreateIncrementalAnalyzer(workspace); - if (analyzer != null) - { - coordinator.AddAnalyzer(analyzer, metadata.HighPriorityForActiveFile); - } - } - } - } - - public void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) - { - lock (_gate) - { - if (!_documentWorkCoordinatorMap.TryGetValue(workspace, out var coordinator)) - { - // this can happen if solution crawler is already unregistered from workspace. - // one of those example will be VS shutting down so roslyn package is disposed but there is a pending - // async operation. - return; - } - - // no specific projects or documents provided - if (projectIds == null && documentIds == null) - { - coordinator.Reanalyze(analyzer, new ReanalyzeScope(workspace.CurrentSolution.Id), highPriority); - return; - } - - coordinator.Reanalyze(analyzer, new ReanalyzeScope(projectIds, documentIds), highPriority); - } - } - - private IEnumerable> GetAnalyzerProviders(string workspaceKind) - { - foreach (var (_, lazyProviders) in _analyzerProviders) - { - // try get provider for the specific workspace kind - if (TryGetProvider(workspaceKind, lazyProviders, out var lazyProvider)) - { - yield return lazyProvider; - continue; - } - - // try get default provider - if (TryGetProvider(Default, lazyProviders, out lazyProvider)) - { - yield return lazyProvider; - } - } - } - - private static bool TryGetProvider( - string kind, - ImmutableArray> lazyProviders, - [NotNullWhen(true)] out Lazy? lazyProvider) - { - // set out param - lazyProvider = null; - - // try find provider for specific workspace kind - if (kind != Default) - { - foreach (var provider in lazyProviders) - { - if (provider.Metadata.WorkspaceKinds.Any(wk => wk == kind)) - { - lazyProvider = provider; - return true; - } - } - - return false; - } - - // try find default provider - foreach (var provider in lazyProviders) - { - if (IsDefaultProvider(provider.Metadata)) - { - lazyProvider = provider; - return true; - } - - return false; - } - - return false; - } - - [Conditional("DEBUG")] - private static void AssertAnalyzerProviders( - ImmutableDictionary>> analyzerProviders) - { -#if DEBUG - // make sure there is duplicated provider defined for same workspace. - var set = new HashSet(); - foreach (var kv in analyzerProviders) - { - foreach (var lazyProvider in kv.Value) - { - if (IsDefaultProvider(lazyProvider.Metadata)) - { - Debug.Assert(set.Add(Default)); - continue; - } - - foreach (var kind in lazyProvider.Metadata.WorkspaceKinds) - { - Debug.Assert(set.Add(kind)); - } - } - - set.Clear(); - } -#endif - } - - private static bool IsDefaultProvider(IncrementalAnalyzerProviderMetadata providerMetadata) - => providerMetadata.WorkspaceKinds is []; - - internal TestAccessor GetTestAccessor() - { - return new TestAccessor(this); - } - - internal readonly struct TestAccessor - { - private readonly SolutionCrawlerRegistrationService _solutionCrawlerRegistrationService; - - internal TestAccessor(SolutionCrawlerRegistrationService solutionCrawlerRegistrationService) - { - _solutionCrawlerRegistrationService = solutionCrawlerRegistrationService; - } - - internal ref ImmutableDictionary>> AnalyzerProviders - => ref _solutionCrawlerRegistrationService._analyzerProviders; - - internal bool TryGetWorkCoordinator(Workspace workspace, [NotNullWhen(true)] out WorkCoordinator? coordinator) - { - lock (_solutionCrawlerRegistrationService._gate) - { - return _solutionCrawlerRegistrationService._documentWorkCoordinatorMap.TryGetValue(workspace, out coordinator); - } - } - - internal void WaitUntilCompletion(Workspace workspace, ImmutableArray workers) - { - if (TryGetWorkCoordinator(workspace, out var coordinator)) - { - coordinator.GetTestAccessor().WaitUntilCompletion(workers); - } - } - - internal void WaitUntilCompletion(Workspace workspace) - { - if (TryGetWorkCoordinator(workspace, out var coordinator)) - { - coordinator.GetTestAccessor().WaitUntilCompletion(); - } - } - } - - internal sealed class Registration(int correlationId, Workspace workspace, SolutionCrawlerProgressReporter progressReporter) - { - public readonly int CorrelationId = correlationId; - public readonly Workspace Workspace = workspace; - public readonly SolutionCrawlerProgressReporter ProgressReporter = progressReporter; - - public Solution GetSolutionToAnalyze() - => Workspace.CurrentSolution; - } - } -} -#endif From 46974be497cd97a62c2b725ce09b1f12f87f80f6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:14 -0800 Subject: [PATCH 079/151] simplify --- .../SolutionCrawler/SolutionCrawlerService.cs | 57 ------------------- 1 file changed, 57 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs deleted file mode 100644 index 16adcbfdc6880..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Generic; -using System.Composition; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService : ISolutionCrawlerRegistrationService -{ - internal static readonly Option2 EnableSolutionCrawler = new("dotnet_enable_solution_crawler", defaultValue: true); - - /// - /// nested class of since it is tightly coupled with it. - /// - /// is implemented by this class since WorkspaceService doesn't allow a class to implement - /// more than one . - /// - [ExportWorkspaceService(typeof(ISolutionCrawlerService), ServiceLayer.Default), Shared] - internal class SolutionCrawlerService : ISolutionCrawlerService - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SolutionCrawlerService() - { - } - - public void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) - { - // if solution crawler doesn't exist for the given workspace. don't do anything - if (workspace.Services.GetService() is SolutionCrawlerRegistrationService registration) - { - registration.Reanalyze(workspace, analyzer, projectIds, documentIds, highPriority); - } - } - - public ISolutionCrawlerProgressReporter GetProgressReporter(Workspace workspace) - { - // if solution crawler doesn't exist for the given workspace, return null reporter - if (workspace.Services.GetService() is SolutionCrawlerRegistrationService registration) - { - // currently we have only 1 global reporter that are shared by all workspaces. - return registration._progressReporter; - } - - return NullReporter.Instance; - } - } -} -#endif From 01fedd9b35fd0d197bc9f37a7f0ba7d7b6516748 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:21 -0800 Subject: [PATCH 080/151] simplify --- .../SolutionCrawlerTimeSpan.cs | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs deleted file mode 100644 index 858fce3f51a1b..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerTimeSpan.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal static class SolutionCrawlerTimeSpan -{ - public static readonly TimeSpan ActiveFileWorkerBackOff = TimeSpan.FromMilliseconds(100); - public static readonly TimeSpan AllFilesWorkerBackOff = TimeSpan.FromMilliseconds(1500); - public static readonly TimeSpan EntireProjectWorkerBackOff = TimeSpan.FromMilliseconds(5000); - public static readonly TimeSpan SemanticChangeBackOff = TimeSpan.FromMilliseconds(100); - public static readonly TimeSpan ProjectPropagationBackOff = TimeSpan.FromMilliseconds(500); - public static readonly TimeSpan PreviewBackOff = TimeSpan.FromMilliseconds(500); -} -#endif From 9e6fa993e4b4c98238d4743758563be2a7d01e85 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:28 -0800 Subject: [PATCH 081/151] simplify --- ...rkCoordinator.AbstractPriorityProcessor.cs | 157 ------------------ 1 file changed, 157 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs deleted file mode 100644 index cfbff5bb16128..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Shared.TestHooks; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal sealed partial class SolutionCrawlerRegistrationService -{ - internal sealed partial class WorkCoordinator - { - private sealed partial class IncrementalAnalyzerProcessor - { - private abstract class AbstractPriorityProcessor : GlobalOperationAwareIdleProcessor - { - protected readonly IncrementalAnalyzerProcessor Processor; - - private readonly object _gate = new(); - private Lazy> _lazyAnalyzers; - - public AbstractPriorityProcessor( - IAsynchronousOperationListener listener, - IncrementalAnalyzerProcessor processor, - Lazy> lazyAnalyzers, - IGlobalOperationNotificationService? globalOperationNotificationService, - TimeSpan backOffTimeSpan, - CancellationToken shutdownToken) - : base(listener, globalOperationNotificationService, backOffTimeSpan, shutdownToken) - { - _lazyAnalyzers = lazyAnalyzers; - - Processor = processor; - Processor._documentTracker.NonRoslynBufferTextChanged += OnNonRoslynBufferTextChanged; - } - - public ImmutableArray Analyzers - { - get - { - lock (_gate) - { - return _lazyAnalyzers.Value; - } - } - } - - public void AddAnalyzer(IIncrementalAnalyzer analyzer) - { - lock (_gate) - { - var analyzers = _lazyAnalyzers.Value; - _lazyAnalyzers = new Lazy>(() => analyzers.Add(analyzer)); - } - } - - protected override void OnPaused() - => SolutionCrawlerLogger.LogGlobalOperation(Processor._logAggregator); - - protected abstract Task HigherQueueOperationTask { get; } - protected abstract bool HigherQueueHasWorkItem { get; } - - protected async Task WaitForHigherPriorityOperationsAsync() - { - using (Logger.LogBlock(FunctionId.WorkCoordinator_WaitForHigherPriorityOperationsAsync, CancellationToken)) - { - while (true) - { - CancellationToken.ThrowIfCancellationRequested(); - - // we wait for global operation and higher queue operation if there is anything going on - await HigherQueueOperationTask.ConfigureAwait(false); - - if (HigherQueueHasWorkItem) - { - // There was still something more important in another queue. Back off again (e.g. - // call UpdateLastAccessTime) then wait that amount of time and check again to see - // if that queue is clear. - UpdateLastAccessTime(); - await WaitForIdleAsync(Listener).ConfigureAwait(false); - continue; - } - - if (GetIsPaused()) - { - // if we're paused, we still want to keep waiting until we become unpaused. After we - // become unpaused though, loop around those to see if there is still high pri work - // to do. - await WaitForIdleAsync(Listener).ConfigureAwait(false); - continue; - } - - // There was no higher queue work item and we're not paused. However, we may not have - // waited long enough to actually satisfy our own backoff-delay. If so, wait until - // we're actually idle. - if (ShouldContinueToBackOff()) - { - // Do the wait. If it returns 'true' then we did the full wait. Loop around again - // to see if there is higher priority work, or if we got paused. - - // However, if it returns 'false' then that means the delay completed quickly - // because some unit/integration test is asking us to expedite our work. In that - // case, just return out immediately so we can process what is in our queue. - if (await WaitForIdleAsync(Listener).ConfigureAwait(false)) - continue; - - // intentional fall-through. - } - - return; - } - } - } - - public override void Shutdown() - { - base.Shutdown(); - - Processor._documentTracker.NonRoslynBufferTextChanged -= OnNonRoslynBufferTextChanged; - - foreach (var analyzer in Analyzers) - { - analyzer.Shutdown(); - } - } - - private void OnNonRoslynBufferTextChanged(object? sender, EventArgs e) - { - // There are 2 things incremental processor takes care of - // - // #1 is making sure we delay processing any work until there is enough idle (ex, typing) in host. - // #2 is managing cancellation and pending works. - // - // we used to do #1 and #2 only for Roslyn files. and that is usually fine since most of time solution contains only roslyn files. - // - // but for mixed solution (ex, Roslyn files + HTML + JS + CSS), #2 still makes sense but #1 doesn't. We want - // to pause any work while something is going on in other project types as well. - // - // we need to make sure we play nice with neighbors as well. - // - // now, we don't care where changes are coming from. if there is any change in host, we pause ourselves for a while. - UpdateLastAccessTime(); - } - } - } - } -} - -#endif From bb72c10fb1c357147a8ba1b31ffdf7ce64617d2f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:36 -0800 Subject: [PATCH 082/151] simplify --- ...kCoordinator.AsyncDocumentWorkItemQueue.cs | 150 ------------------ 1 file changed, 150 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs deleted file mode 100644 index e083f4f7fe158..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System.Collections.Generic; -using System.Diagnostics; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Internal.Log; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService -{ - internal partial class WorkCoordinator - { - private class AsyncDocumentWorkItemQueue(SolutionCrawlerProgressReporter progressReporter, Workspace workspace) : AsyncWorkItemQueue(progressReporter, workspace) - { - private readonly Dictionary> _documentWorkQueue = []; - - protected override int WorkItemCount_NoLock => _documentWorkQueue.Count; - - protected override bool TryTake_NoLock(DocumentId key, out WorkItem workInfo) - { - workInfo = default; - if (_documentWorkQueue.TryGetValue(key.ProjectId, out var documentMap) && - documentMap.TryGetValue(key, out workInfo)) - { - documentMap.Remove(key); - - if (documentMap.Count == 0) - { - _documentWorkQueue.Remove(key.ProjectId); - SharedPools.BigDefault>().ClearAndFree(documentMap); - } - - return true; - } - - return false; - } - - protected override bool TryTakeAnyWork_NoLock( - ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, - out WorkItem workItem) - { - // there must be at least one item in the map when this is called unless host is shutting down. - if (_documentWorkQueue.Count == 0) - { - workItem = default; - return false; - } - - var documentId = GetBestDocumentId_NoLock(preferableProjectId, dependencyGraph); - if (TryTake_NoLock(documentId, out workItem)) - { - return true; - } - - throw ExceptionUtilities.Unreachable(); - } - - private DocumentId GetBestDocumentId_NoLock( - ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph) - { - var projectId = GetBestProjectId_NoLock(_documentWorkQueue, preferableProjectId, dependencyGraph); - - var documentMap = _documentWorkQueue[projectId]; - - // explicitly iterate so that we can use struct enumerator. - // Return the first normal priority work item we find. If we don't - // find any, then just return the first low prio item we saw. - DocumentId? lowPriorityDocumentId = null; - foreach (var (documentId, workItem) in documentMap) - { - if (workItem.IsLowPriority) - { - lowPriorityDocumentId = documentId; - } - else - { - return documentId; - } - } - - Contract.ThrowIfNull(lowPriorityDocumentId); - return lowPriorityDocumentId; - } - - protected override bool AddOrReplace_NoLock(WorkItem item) - { - Contract.ThrowIfNull(item.DocumentId); - - Cancel_NoLock(item.DocumentId); - - // see whether we need to update - var key = item.DocumentId; - - // now document work - if (_documentWorkQueue.TryGetValue(key.ProjectId, out var documentMap) && - documentMap.TryGetValue(key, out var existingWorkItem)) - { - // TODO: should I care about language when replace it? - Debug.Assert(existingWorkItem.Language == item.Language); - - // replace it - documentMap[key] = existingWorkItem.With(item.InvocationReasons, item.ActiveMember, item.SpecificAnalyzers, item.AsyncToken); - return false; - } - - // add document map if it is not already there - if (documentMap == null) - { - documentMap = SharedPools.BigDefault>().AllocateAndClear(); - _documentWorkQueue.Add(key.ProjectId, documentMap); - - if (_documentWorkQueue.Count == 1) - { - Logger.Log(FunctionId.WorkCoordinator_AsyncWorkItemQueue_FirstItem); - } - } - - // okay, it is new one - // always hold onto the most recent one for the same document - documentMap.Add(key, item); - - return true; - } - - protected override void Dispose_NoLock() - { - foreach (var map in _documentWorkQueue.Values) - { - foreach (var workItem in map.Values) - { - workItem.AsyncToken.Dispose(); - } - - SharedPools.BigDefault>().ClearAndFree(map); - } - - _documentWorkQueue.Clear(); - } - } - } -} - -#endif From e779dfa95122e5a585f81662fbeface1419d60ae Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:42 -0800 Subject: [PATCH 083/151] simplify --- ...rkCoordinator.AsyncProjectWorkItemQueue.cs | 100 ------------------ 1 file changed, 100 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs deleted file mode 100644 index e94d81ab2d0a2..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Internal.Log; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService -{ - internal partial class WorkCoordinator - { - private sealed class AsyncProjectWorkItemQueue(SolutionCrawlerProgressReporter progressReporter, Workspace workspace) : AsyncWorkItemQueue(progressReporter, workspace) - { - private readonly Dictionary _projectWorkQueue = []; - - protected override int WorkItemCount_NoLock => _projectWorkQueue.Count; - - public override Task WaitAsync(CancellationToken cancellationToken) - { - if (!HasAnyWork) - { - Logger.Log(FunctionId.WorkCoordinator_AsyncWorkItemQueue_LastItem); - } - - return base.WaitAsync(cancellationToken); - } - - protected override bool TryTake_NoLock(ProjectId key, out WorkItem workInfo) - { - if (!_projectWorkQueue.TryGetValue(key, out workInfo)) - { - workInfo = default; - return false; - } - - return _projectWorkQueue.Remove(key); - } - - protected override bool TryTakeAnyWork_NoLock( - ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, - out WorkItem workItem) - { - // there must be at least one item in the map when this is called unless host is shutting down. - if (_projectWorkQueue.Count == 0) - { - workItem = default; - return false; - } - - var projectId = GetBestProjectId_NoLock(_projectWorkQueue, preferableProjectId, dependencyGraph); - if (TryTake_NoLock(projectId, out workItem)) - { - return true; - } - - throw ExceptionUtilities.Unreachable(); - } - - protected override bool AddOrReplace_NoLock(WorkItem item) - { - var key = item.ProjectId; - Cancel_NoLock(key); - // now document work - - // see whether we need to update - if (_projectWorkQueue.TryGetValue(key, out var existingWorkItem)) - { - // replace it. - _projectWorkQueue[key] = existingWorkItem.With(item.InvocationReasons, item.ActiveMember, item.SpecificAnalyzers, item.AsyncToken); - return false; - } - - // okay, it is new one - // always hold onto the most recent one for the same project - _projectWorkQueue.Add(key, item); - - return true; - } - - protected override void Dispose_NoLock() - { - foreach (var workItem in _projectWorkQueue.Values) - { - workItem.AsyncToken.Dispose(); - } - - _projectWorkQueue.Clear(); - } - } - } -} -#endif From d98087b9e096659c69759687a68aa3511b8c053e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:49 -0800 Subject: [PATCH 084/151] simplify --- .../WorkCoordinator.AsyncWorkItemQueue.cs | 278 ------------------ 1 file changed, 278 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs deleted file mode 100644 index 27b708c993d7a..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs +++ /dev/null @@ -1,278 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService -{ - internal partial class WorkCoordinator - { - private abstract class AsyncWorkItemQueue(SolutionCrawlerProgressReporter progressReporter, Workspace workspace) : IDisposable - where TKey : class - { - private readonly object _gate = new(); - private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(initialCount: 0); - private bool _disposed; - - private readonly Workspace _workspace = workspace; - private readonly SolutionCrawlerProgressReporter _progressReporter = progressReporter; - - // map containing cancellation source for the item given out. - private readonly Dictionary _cancellationMap = []; - - protected abstract int WorkItemCount_NoLock { get; } - - protected abstract void Dispose_NoLock(); - - protected abstract bool AddOrReplace_NoLock(WorkItem item); - - protected abstract bool TryTake_NoLock(TKey key, out WorkItem workInfo); - - protected abstract bool TryTakeAnyWork_NoLock(ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem); - - public int WorkItemCount - { - get - { - lock (_gate) - { - return WorkItemCount_NoLock; - } - } - } - - public bool HasAnyWork - { - get - { - lock (_gate) - { - return WorkItemCount_NoLock > 0; - } - } - } - - public virtual Task WaitAsync(CancellationToken cancellationToken) - => _semaphore.WaitAsync(cancellationToken); - - public bool AddOrReplace(WorkItem item) - { - lock (_gate) - { - if (_disposed) - { - // The work queue was shut down, so mark the request as complete and return false to - // indicate the work was not queued. - item.AsyncToken.Dispose(); - return false; - } - - if (AddOrReplace_NoLock(item)) - { - // the item is new item that got added to the queue. - // let solution crawler progress report to know about new item enqueued. - // progress reporter will take care of nested/overlapped works by itself - // - // order of events is as follow - // 1. first item added by AddOrReplace which is the point where progress start. - // 2. bunch of other items added or replaced (workitem in the queue > 0) - // 3. items start dequeued to be processed by TryTake or TryTakeAnyWork - // 4. once item is done processed, it is marked as done by MarkWorkItemDoneFor - // 5. all items in the queue are dequeued (workitem in the queue == 0) - // but there can be still work in progress - // 6. all works are considered done when last item is marked done by MarkWorkItemDoneFor - // and at the point, we will set progress to stop. - _progressReporter.Start(); - - // increase count - _semaphore.Release(); - return true; - } - - return false; - } - } - - public void MarkWorkItemDoneFor(object key) - { - lock (_gate) - { - // just remove cancellation token from the map. - // the cancellation token might be passed out to other service - // so don't call cancel on the source only because we are done using it. - _cancellationMap.Remove(key); - - // every works enqueued by "AddOrReplace" will be processed - // at some point, and when it is processed, this method will be called to mark - // work has been done. - _progressReporter.Stop(); - } - } - - public void RequestCancellationOnRunningTasks() - { - List? cancellations; - lock (_gate) - { - // request to cancel all running works - cancellations = CancelAll_NoLock(); - } - - RaiseCancellation_NoLock(cancellations); - } - - public void Dispose() - { - List? cancellations; - lock (_gate) - { - _disposed = true; - - // here we don't need to care about progress reporter since - // it will be only called when host is shutting down. - // we do the below since we want to kill any pending tasks - Dispose_NoLock(); - - cancellations = CancelAll_NoLock(); - } - - RaiseCancellation_NoLock(cancellations); - } - - protected Workspace Workspace => _workspace; - - private static void RaiseCancellation_NoLock(List? cancellations) - { - if (cancellations == null) - { - return; - } - - // cancel can cause outer code to be run inlined, run it outside of the lock. - cancellations.Do(s => s.Cancel()); - } - - private List? CancelAll_NoLock() - { - // nothing to do - if (_cancellationMap.Count == 0) - { - return null; - } - - // make a copy - var cancellations = _cancellationMap.Values.ToList(); - - // clear cancellation map - _cancellationMap.Clear(); - - return cancellations; - } - - protected void Cancel_NoLock(object key) - { - if (_cancellationMap.TryGetValue(key, out var source)) - { - source.Cancel(); - _cancellationMap.Remove(key); - } - } - - public bool TryTake(TKey key, out WorkItem workInfo, out CancellationToken cancellationToken) - { - lock (_gate) - { - if (TryTake_NoLock(key, out workInfo)) - { - cancellationToken = GetNewCancellationToken_NoLock(key); - workInfo.AsyncToken.Dispose(); - return true; - } - else - { - cancellationToken = CancellationToken.None; - return false; - } - } - } - - public bool TryTakeAnyWork( - ProjectId? preferableProjectId, - ProjectDependencyGraph dependencyGraph, - out WorkItem workItem, - out CancellationToken cancellationToken) - { - lock (_gate) - { - // there must be at least one item in the map when this is called unless host is shutting down. - if (TryTakeAnyWork_NoLock(preferableProjectId, dependencyGraph, out workItem)) - { - cancellationToken = GetNewCancellationToken_NoLock(workItem.Key); - workItem.AsyncToken.Dispose(); - return true; - } - else - { - cancellationToken = CancellationToken.None; - return false; - } - } - } - - protected CancellationToken GetNewCancellationToken_NoLock(object key) - { - Debug.Assert(!_cancellationMap.ContainsKey(key)); - - var source = new CancellationTokenSource(); - _cancellationMap.Add(key, source); - - return source.Token; - } - - protected static ProjectId GetBestProjectId_NoLock( - Dictionary workQueue, ProjectId? projectId, - ProjectDependencyGraph dependencyGraph) - { - if (projectId != null) - { - if (workQueue.ContainsKey(projectId)) - { - return projectId; - } - - // prefer project that directly depends on the given project as next project to - // process - foreach (var dependingProjectId in dependencyGraph.GetProjectsThatDirectlyDependOnThisProject(projectId)) - { - if (workQueue.ContainsKey(dependingProjectId)) - { - return dependingProjectId; - } - } - } - - // explicitly iterate so that we can use struct enumerator - foreach (var pair in workQueue) - { - return pair.Key; - } - - throw ExceptionUtilities.Unreachable(); - } - } - } -} - -#endif From fee50ab63730f9d7aa73cda0d9c7cd3fd771e808 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:57:56 -0800 Subject: [PATCH 085/151] simplify --- .../WorkCoordinator.HighPriorityProcessor.cs | 239 ------------------ 1 file changed, 239 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs deleted file mode 100644 index 0ac7b6eedf37a..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs +++ /dev/null @@ -1,239 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal sealed partial class SolutionCrawlerRegistrationService -{ - internal sealed partial class WorkCoordinator - { - private sealed partial class IncrementalAnalyzerProcessor - { - private sealed class HighPriorityProcessor : IdleProcessor - { - private readonly IncrementalAnalyzerProcessor _processor; - private readonly AsyncDocumentWorkItemQueue _workItemQueue; - private readonly object _gate = new(); - - private Lazy> _lazyAnalyzers; - - // whether this processor is running or not - private Task _running; - - public HighPriorityProcessor( - IAsynchronousOperationListener listener, - IncrementalAnalyzerProcessor processor, - Lazy> lazyAnalyzers, - TimeSpan backOffTimeSpan, - CancellationToken shutdownToken) - : base(listener, backOffTimeSpan, shutdownToken) - { - _processor = processor; - _lazyAnalyzers = lazyAnalyzers; - - _running = Task.CompletedTask; - _workItemQueue = new AsyncDocumentWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace); - - Start(); - } - - protected override void OnPaused() - { - } - - public ImmutableArray Analyzers - { - get - { - lock (_gate) - { - return _lazyAnalyzers.Value; - } - } - } - - public Task Running => _running; - - public int WorkItemCount => _workItemQueue.WorkItemCount; - public bool HasAnyWork => _workItemQueue.HasAnyWork; - - public void AddAnalyzer(IIncrementalAnalyzer analyzer) - { - lock (_gate) - { - var analyzers = _lazyAnalyzers.Value; - _lazyAnalyzers = new Lazy>(() => analyzers.Add(analyzer)); - } - } - - public void Enqueue(WorkItem item) - { - Contract.ThrowIfFalse(item.DocumentId != null, "can only enqueue a document work item"); - - // Don't enqueue item if we don't have any high priority analyzers - if (Analyzers.IsEmpty) - { - return; - } - - // we only put workitem in high priority queue if there is a text change. - // this is to prevent things like opening a file, changing in other files keep enqueuing - // expensive high priority work. - if (!item.InvocationReasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) - { - return; - } - - if (!_processor._documentTracker.SupportsDocumentTracking - && _processor._registration.Workspace.Kind is WorkspaceKind.RemoteWorkspace) - { - Debug.Fail($"Unexpected use of '{nameof(ExportIncrementalAnalyzerProviderAttribute.HighPriorityForActiveFile)}' in workspace kind '{_processor._registration.Workspace.Kind}' that cannot support active file tracking."); - } - - // check whether given item is for active document, otherwise, nothing to do here - if (_processor._documentTracker.TryGetActiveDocument() != item.DocumentId) - { - return; - } - - // we need to clone due to waiter - EnqueueActiveFileItem(item.WithAsyncToken(Listener.BeginAsyncOperation("ActiveFile"))); - } - - private void EnqueueActiveFileItem(WorkItem item) - { - Contract.ThrowIfNull(item.DocumentId); - - UpdateLastAccessTime(); - var added = _workItemQueue.AddOrReplace(item); - - Logger.Log(FunctionId.WorkCoordinator_ActiveFileEnqueue, s_enqueueLogger, Environment.TickCount, item.DocumentId, !added); - SolutionCrawlerLogger.LogActiveFileEnqueue(_processor._logAggregator); - } - - protected override Task WaitAsync(CancellationToken cancellationToken) - => _workItemQueue.WaitAsync(cancellationToken); - - protected override async Task ExecuteAsync() - { - Debug.Assert(!Analyzers.IsEmpty); - - if (CancellationToken.IsCancellationRequested) - { - return; - } - - var source = new TaskCompletionSource(); - try - { - // mark it as running - _running = source.Task; - // okay, there must be at least one item in the map - // see whether we have work item for the document - Contract.ThrowIfFalse(GetNextWorkItem(out var workItem, out var documentCancellation)); - - var solution = _processor._registration.GetSolutionToAnalyze(); - - // okay now we have work to do - await ProcessDocumentAsync(solution, Analyzers, workItem, documentCancellation).ConfigureAwait(false); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable(); - } - finally - { - // mark it as done running - source.SetResult(null); - } - } - - private bool GetNextWorkItem(out WorkItem workItem, out CancellationToken cancellationToken) - { - // GetNextWorkItem since it can't fail. we still return bool to confirm that this never fail. - var documentId = _processor._documentTracker.TryGetActiveDocument(); - if (documentId != null) - { - if (_workItemQueue.TryTake(documentId, out workItem, out cancellationToken)) - { - return true; - } - } - - return _workItemQueue.TryTakeAnyWork( - preferableProjectId: null, - dependencyGraph: _processor.DependencyGraph, - workItem: out workItem, - cancellationToken: out cancellationToken); - } - - private async Task ProcessDocumentAsync(Solution solution, ImmutableArray analyzers, WorkItem workItem, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(workItem.DocumentId); - - if (CancellationToken.IsCancellationRequested) - { - return; - } - - var processedEverything = false; - var documentId = workItem.DocumentId; - - try - { - using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessDocumentAsync, w => w.ToString(), workItem, cancellationToken)) - { - var document = solution.GetDocument(documentId); - if (document != null) - { - await _processor.ProcessDocumentAnalyzersAsync(document, analyzers, workItem, cancellationToken).ConfigureAwait(false); - } - - if (!cancellationToken.IsCancellationRequested) - { - processedEverything = true; - } - } - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - finally - { - // we got cancelled in the middle of processing the document. - // let's make sure newly enqueued work item has all the flag needed. - // Avoid retry attempts after cancellation is requested, since work will not be processed - // after that point. - if (!processedEverything && !CancellationToken.IsCancellationRequested) - { - _workItemQueue.AddOrReplace(workItem.WithAsyncToken(Listener.BeginAsyncOperation("ReenqueueWorkItem"))); - } - - SolutionCrawlerLogger.LogProcessActiveFileDocument(_processor._logAggregator, documentId.Id, processedEverything); - - // remove one that is finished running - _workItemQueue.MarkWorkItemDoneFor(workItem.DocumentId); - } - } - - public void Shutdown() - => _workItemQueue.Dispose(); - } - } - } -} -#endif From 53c75fe545d4843b069da786143b0229c3cc9413 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:58:02 -0800 Subject: [PATCH 086/151] simplify --- ...oordinator.IncrementalAnalyzerProcessor.cs | 409 ------------------ 1 file changed, 409 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs deleted file mode 100644 index 1708622d507a7..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs +++ /dev/null @@ -1,409 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService -{ - internal partial class WorkCoordinator - { - private partial class IncrementalAnalyzerProcessor - { - private static readonly Func s_enqueueLogger = EnqueueLogger; - - private readonly Registration _registration; - private readonly IAsynchronousOperationListener _listener; - private readonly IDocumentTrackingService _documentTracker; - - private readonly HighPriorityProcessor _highPriorityProcessor; - private readonly NormalPriorityProcessor _normalPriorityProcessor; - private readonly LowPriorityProcessor _lowPriorityProcessor; - - /// - /// The keys in this are either a string or a (string, Guid) tuple. See - /// for what is writing this out. - /// - private CountLogAggregator _logAggregator = new(); - - public IncrementalAnalyzerProcessor( - IAsynchronousOperationListener listener, - IEnumerable> analyzerProviders, - bool initializeLazily, - Registration registration, - TimeSpan highBackOffTimeSpan, - TimeSpan normalBackOffTimeSpan, - TimeSpan lowBackOffTimeSpan, - CancellationToken shutdownToken) - { - _listener = listener; - _registration = registration; - - var analyzersGetter = new AnalyzersGetter(analyzerProviders); - - // create analyzers lazily. - var lazyActiveFileAnalyzers = new Lazy>(() => GetIncrementalAnalyzers(_registration, analyzersGetter, onlyHighPriorityAnalyzer: true)); - var lazyAllAnalyzers = new Lazy>(() => GetIncrementalAnalyzers(_registration, analyzersGetter, onlyHighPriorityAnalyzer: false)); - - if (!initializeLazily) - { - // realize all analyzer right away - _ = lazyActiveFileAnalyzers.Value; - _ = lazyAllAnalyzers.Value; - } - - // event and worker queues - _documentTracker = _registration.Workspace.Services.GetRequiredService(); - - var globalNotificationService = _registration.Workspace.Services.SolutionServices.ExportProvider - .GetExports().FirstOrDefault()?.Value; - - _highPriorityProcessor = new HighPriorityProcessor(listener, this, lazyActiveFileAnalyzers, highBackOffTimeSpan, shutdownToken); - _normalPriorityProcessor = new NormalPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, normalBackOffTimeSpan, shutdownToken); - _lowPriorityProcessor = new LowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpan, shutdownToken); - } - - private static ImmutableArray GetIncrementalAnalyzers(Registration registration, AnalyzersGetter analyzersGetter, bool onlyHighPriorityAnalyzer) - { - var orderedAnalyzers = analyzersGetter.GetOrderedAnalyzers(registration.Workspace, onlyHighPriorityAnalyzer); - - SolutionCrawlerLogger.LogAnalyzers(registration.CorrelationId, registration.Workspace, orderedAnalyzers, onlyHighPriorityAnalyzer); - return orderedAnalyzers; - } - - public void Enqueue(WorkItem item) - { - Contract.ThrowIfNull(item.DocumentId); - - _highPriorityProcessor.Enqueue(item); - _normalPriorityProcessor.Enqueue(item); - _lowPriorityProcessor.Enqueue(item); - - ReportPendingWorkItemCount(); - } - - public void AddAnalyzer(IIncrementalAnalyzer analyzer, bool highPriorityForActiveFile) - { - if (highPriorityForActiveFile) - { - _highPriorityProcessor.AddAnalyzer(analyzer); - } - - _normalPriorityProcessor.AddAnalyzer(analyzer); - _lowPriorityProcessor.AddAnalyzer(analyzer); - } - - public void Shutdown() - { - _highPriorityProcessor.Shutdown(); - _normalPriorityProcessor.Shutdown(); - _lowPriorityProcessor.Shutdown(); - } - - public ImmutableArray Analyzers => _normalPriorityProcessor.Analyzers; - - private ProjectDependencyGraph DependencyGraph => _registration.GetSolutionToAnalyze().GetProjectDependencyGraph(); - - public Task AsyncProcessorTask - { - get - { - return Task.WhenAll( - _highPriorityProcessor.AsyncProcessorTask, - _normalPriorityProcessor.AsyncProcessorTask, - _lowPriorityProcessor.AsyncProcessorTask); - } - } - - private IEnumerable GetOpenDocumentIds() - => _registration.Workspace.GetOpenDocumentIds(); - - private void ResetLogAggregator() - => _logAggregator = new CountLogAggregator(); - - private void ReportPendingWorkItemCount() - { - var pendingItemCount = _highPriorityProcessor.WorkItemCount + _normalPriorityProcessor.WorkItemCount + _lowPriorityProcessor.WorkItemCount; - _registration.ProgressReporter.UpdatePendingItemCount(pendingItemCount); - } - - private async Task ProcessDocumentAnalyzersAsync( - TextDocument textDocument, ImmutableArray analyzers, WorkItem workItem, CancellationToken cancellationToken) - { - // process special active document switched request, if any. - if (await ProcessActiveDocumentSwitchedAsync(analyzers, workItem, textDocument, cancellationToken).ConfigureAwait(false)) - { - return; - } - - // process all analyzers for each categories in this order - syntax, body, document - var reasons = workItem.InvocationReasons; - if (workItem.MustRefresh || reasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) - { - await RunAnalyzersAsync(analyzers, textDocument, workItem, (analyzer, document, cancellationToken) => - AnalyzeSyntaxAsync(analyzer, document, reasons, cancellationToken), cancellationToken).ConfigureAwait(false); - } - - if (textDocument is not Document document) - { - // Semantic analysis is not supported for non-source documents. - return; - } - - if (workItem.MustRefresh || reasons.Contains(PredefinedInvocationReasons.SemanticChanged)) - { - await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => - analyzer.AnalyzeDocumentAsync(document, null, reasons, cancellationToken), cancellationToken).ConfigureAwait(false); - } - else - { - // if we don't need to re-analyze whole body, see whether we need to at least re-analyze one method. - await RunBodyAnalyzersAsync(analyzers, workItem, document, cancellationToken).ConfigureAwait(false); - } - - return; - - static async Task AnalyzeSyntaxAsync(IIncrementalAnalyzer analyzer, TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) - { - if (textDocument is Document document) - { - await analyzer.AnalyzeSyntaxAsync(document, reasons, cancellationToken).ConfigureAwait(false); - } - else - { - await analyzer.AnalyzeNonSourceDocumentAsync(textDocument, reasons, cancellationToken).ConfigureAwait(false); - } - } - - async Task ProcessActiveDocumentSwitchedAsync(ImmutableArray analyzers, WorkItem workItem, TextDocument document, CancellationToken cancellationToken) - { - try - { - if (!workItem.InvocationReasons.Contains(PredefinedInvocationReasons.ActiveDocumentSwitched)) - { - return false; - } - - await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => - analyzer.ActiveDocumentSwitchedAsync(document, cancellationToken), cancellationToken).ConfigureAwait(false); - return true; - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - } - } - - private async Task RunAnalyzersAsync( - ImmutableArray analyzers, - T value, - WorkItem workItem, - Func runnerAsync, - CancellationToken cancellationToken) - { - using var evaluating = _registration.ProgressReporter.GetEvaluatingScope(); - - ReportPendingWorkItemCount(); - - // Check if the work item is specific to some incremental analyzer(s). - var analyzersToExecute = workItem.GetApplicableAnalyzers(analyzers) ?? analyzers; - foreach (var analyzer in analyzersToExecute) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - - var local = analyzer; - if (local == null) - { - return; - } - - await GetOrDefaultAsync(value, async (v, c) => - { - await runnerAsync(local, v, c).ConfigureAwait(false); - return (object?)null; - }, cancellationToken).ConfigureAwait(false); - } - } - - private async Task RunBodyAnalyzersAsync(ImmutableArray analyzers, WorkItem workItem, Document document, CancellationToken cancellationToken) - { - try - { - var root = await GetOrDefaultAsync(document, (d, c) => d.GetSyntaxRootAsync(c), cancellationToken).ConfigureAwait(false); - var syntaxFactsService = document.GetLanguageService(); - var reasons = workItem.InvocationReasons; - if (root == null || syntaxFactsService == null) - { - // as a fallback mechanism, if we can't run one method body due to some missing service, run whole document analyzer. - await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => - analyzer.AnalyzeDocumentAsync(document, null, reasons, cancellationToken), cancellationToken).ConfigureAwait(false); - return; - } - - // check whether we know what body has changed. currently, this is an optimization toward typing case. if there are more than one body changes - // it will be considered as semantic change and whole document analyzer will take care of that case. - var activeMember = GetMemberNode(syntaxFactsService, root, workItem.ActiveMember); - if (activeMember == null) - { - // no active member means, change is out side of a method body, but it didn't affect semantics (such as change in comment) - // in that case, we update whole document (just this document) so that we can have updated locations. - await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => - analyzer.AnalyzeDocumentAsync(document, null, reasons, cancellationToken), cancellationToken).ConfigureAwait(false); - return; - } - - // re-run just the body - await RunAnalyzersAsync(analyzers, document, workItem, (analyzer, document, cancellationToken) => - analyzer.AnalyzeDocumentAsync(document, activeMember, reasons, cancellationToken), cancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - } - - private static async Task GetOrDefaultAsync(TData value, Func> funcAsync, CancellationToken cancellationToken) - where TResult : class - { - try - { - return await funcAsync(value, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - return null; - } - catch (AggregateException e) when (ReportWithoutCrashUnlessAllCanceledAndPropagate(e)) - { - return null; - } - catch (Exception e) when (FatalError.ReportAndPropagate(e)) - { - // TODO: manage bad workers like what code actions does now - throw ExceptionUtilities.Unreachable(); - } - - static bool ReportWithoutCrashUnlessAllCanceledAndPropagate(AggregateException aggregate) - { - var flattened = aggregate.Flatten(); - if (flattened.InnerExceptions.All(e => e is OperationCanceledException)) - { - return true; - } - - return FatalError.ReportAndPropagate(flattened); - } - } - - private static SyntaxNode? GetMemberNode(ISyntaxFactsService service, SyntaxNode? root, SyntaxPath? memberPath) - { - if (root == null || memberPath == null) - { - return null; - } - - if (!memberPath.TryResolve(root, out SyntaxNode? memberNode)) - { - return null; - } - - return service.IsMethodLevelMember(memberNode) ? memberNode : null; - } - - private static string EnqueueLogger(int tick, object documentOrProjectId, bool replaced) - { - if (documentOrProjectId is DocumentId documentId) - { - return $"Tick:{tick}, {documentId}, {documentId.ProjectId}, Replaced:{replaced}"; - } - - return $"Tick:{tick}, {documentOrProjectId}, Replaced:{replaced}"; - } - - internal TestAccessor GetTestAccessor() - { - return new TestAccessor(this); - } - - internal readonly struct TestAccessor - { - private readonly IncrementalAnalyzerProcessor _incrementalAnalyzerProcessor; - - internal TestAccessor(IncrementalAnalyzerProcessor incrementalAnalyzerProcessor) - { - _incrementalAnalyzerProcessor = incrementalAnalyzerProcessor; - } - - internal void WaitUntilCompletion(ImmutableArray analyzers, List items) - { - _incrementalAnalyzerProcessor._normalPriorityProcessor.GetTestAccessor().WaitUntilCompletion(analyzers, items); - - var projectItems = items.Select(i => i.ToProjectWorkItem(EmptyAsyncToken.Instance)); - _incrementalAnalyzerProcessor._lowPriorityProcessor.GetTestAccessor().WaitUntilCompletion(analyzers, items); - } - - internal void WaitUntilCompletion() - { - _incrementalAnalyzerProcessor._normalPriorityProcessor.GetTestAccessor().WaitUntilCompletion(); - _incrementalAnalyzerProcessor._lowPriorityProcessor.GetTestAccessor().WaitUntilCompletion(); - } - } - - private class AnalyzersGetter(IEnumerable> analyzerProviders) - { - private readonly List> _analyzerProviders = analyzerProviders.ToList(); - private readonly Dictionary> _analyzerMap = []; - - public ImmutableArray GetOrderedAnalyzers(Workspace workspace, bool onlyHighPriorityAnalyzer) - { - lock (_analyzerMap) - { - if (!_analyzerMap.TryGetValue(workspace, out var analyzers)) - { - // Sort list so DiagnosticIncrementalAnalyzers (if any) come first. - analyzers = _analyzerProviders.Select(p => (analyzer: p.Value.CreateIncrementalAnalyzer(workspace), highPriorityForActiveFile: p.Metadata.HighPriorityForActiveFile)) - .Where(t => t.analyzer != null) - .OrderBy(t => t.analyzer!.Priority) - .ToImmutableArray()!; - - _analyzerMap[workspace] = analyzers; - } - - if (onlyHighPriorityAnalyzer) - { - // include only high priority analyzer for active file - return analyzers.SelectAsArray(t => t.highPriorityForActiveFile, t => t.analyzer); - } - - // return all analyzers - return analyzers.Select(t => t.analyzer).ToImmutableArray(); - } - } - } - } - } -} -#endif From a0b59c373f9ba4a5a9f38383b8adf1528c856f6b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:58:08 -0800 Subject: [PATCH 087/151] simplify --- .../WorkCoordinator.LowPriorityProcessor.cs | 238 ------------------ 1 file changed, 238 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs deleted file mode 100644 index c7d6baaf8be2b..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs +++ /dev/null @@ -1,238 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal sealed partial class SolutionCrawlerRegistrationService -{ - internal sealed partial class WorkCoordinator - { - private sealed partial class IncrementalAnalyzerProcessor - { - private sealed class LowPriorityProcessor : AbstractPriorityProcessor - { - private readonly AsyncProjectWorkItemQueue _workItemQueue; - - public LowPriorityProcessor( - IAsynchronousOperationListener listener, - IncrementalAnalyzerProcessor processor, - Lazy> lazyAnalyzers, - IGlobalOperationNotificationService? globalOperationNotificationService, - TimeSpan backOffTimeSpan, - CancellationToken shutdownToken) - : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) - { - _workItemQueue = new AsyncProjectWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace); - - Start(); - } - - public int WorkItemCount => _workItemQueue.WorkItemCount; - - protected override Task WaitAsync(CancellationToken cancellationToken) - => _workItemQueue.WaitAsync(cancellationToken); - - protected override async Task ExecuteAsync() - { - try - { - // we wait for global operation, higher and normal priority processor to finish its working - await WaitForHigherPriorityOperationsAsync().ConfigureAwait(false); - - // process any available project work, preferring the active project. - var preferableProjectId = Processor._documentTracker.SupportsDocumentTracking - ? Processor._documentTracker.TryGetActiveDocument()?.ProjectId - : null; - - if (_workItemQueue.TryTakeAnyWork( - preferableProjectId, Processor.DependencyGraph, - out var workItem, out var projectCancellation)) - { - await ProcessProjectAsync(Analyzers, workItem, projectCancellation).ConfigureAwait(false); - } - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable(); - } - } - - protected override Task HigherQueueOperationTask - { - get - { - return Task.WhenAll(Processor._highPriorityProcessor.Running, Processor._normalPriorityProcessor.Running); - } - } - - protected override bool HigherQueueHasWorkItem - { - get - { - return Processor._highPriorityProcessor.HasAnyWork || Processor._normalPriorityProcessor.HasAnyWork; - } - } - - protected override void OnPaused() - { - base.OnPaused(); - - _workItemQueue.RequestCancellationOnRunningTasks(); - } - - public void Enqueue(WorkItem item) - { - UpdateLastAccessTime(); - - // Project work - item = item.ToProjectWorkItem(Processor._listener.BeginAsyncOperation("WorkItem")); - - var added = _workItemQueue.AddOrReplace(item); - - // lower priority queue gets lowest time slot possible. if there is any activity going on in higher queue, it drop whatever it has - // and let higher work item run - CancelRunningTaskIfHigherQueueHasWorkItem(); - - Logger.Log(FunctionId.WorkCoordinator_Project_Enqueue, s_enqueueLogger, Environment.TickCount, item.ProjectId, !added); - - SolutionCrawlerLogger.LogWorkItemEnqueue(Processor._logAggregator, item.ProjectId); - } - - private void CancelRunningTaskIfHigherQueueHasWorkItem() - { - if (!HigherQueueHasWorkItem) - { - return; - } - - _workItemQueue.RequestCancellationOnRunningTasks(); - } - - private async Task ProcessProjectAsync(ImmutableArray analyzers, WorkItem workItem, CancellationToken cancellationToken) - { - if (CancellationToken.IsCancellationRequested) - { - return; - } - - // we do have work item for this project - var projectId = workItem.ProjectId; - var processedEverything = false; - var processingSolution = Processor._registration.GetSolutionToAnalyze(); - - try - { - using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessProjectAsync, w => w.ToString(), workItem, cancellationToken)) - { - var project = processingSolution.GetProject(projectId); - if (project != null) - { - var reasons = workItem.InvocationReasons; - var semanticsChanged = reasons.Contains(PredefinedInvocationReasons.SemanticChanged) || - reasons.Contains(PredefinedInvocationReasons.SolutionRemoved); - - await Processor.RunAnalyzersAsync(analyzers, project, workItem, (a, p, c) => a.AnalyzeProjectAsync(p, semanticsChanged, reasons, c), cancellationToken).ConfigureAwait(false); - } - else - { - SolutionCrawlerLogger.LogProcessProjectNotExist(Processor._logAggregator); - - await RemoveProjectAsync(projectId, cancellationToken).ConfigureAwait(false); - } - - if (!cancellationToken.IsCancellationRequested) - { - processedEverything = true; - } - } - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - finally - { - // we got cancelled in the middle of processing the project. - // let's make sure newly enqueued work item has all the flag needed. - // Avoid retry attempts after cancellation is requested, since work will not be processed - // after that point. - if (!processedEverything && !CancellationToken.IsCancellationRequested) - { - _workItemQueue.AddOrReplace(workItem.WithAsyncToken(Listener.BeginAsyncOperation("ReenqueueWorkItem"))); - } - - SolutionCrawlerLogger.LogProcessProject(Processor._logAggregator, projectId.Id, processedEverything); - - // remove one that is finished running - _workItemQueue.MarkWorkItemDoneFor(projectId); - } - } - - private async Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) - { - foreach (var analyzer in Analyzers) - { - await analyzer.RemoveProjectAsync(projectId, cancellationToken).ConfigureAwait(false); - } - } - - public override void Shutdown() - { - base.Shutdown(); - _workItemQueue.Dispose(); - } - - internal TestAccessor GetTestAccessor() - { - return new TestAccessor(this); - } - - internal readonly struct TestAccessor - { - private readonly LowPriorityProcessor _lowPriorityProcessor; - - internal TestAccessor(LowPriorityProcessor lowPriorityProcessor) - { - _lowPriorityProcessor = lowPriorityProcessor; - } - - internal void WaitUntilCompletion(ImmutableArray analyzers, List items) - { - var uniqueIds = new HashSet(); - foreach (var item in items) - { - if (uniqueIds.Add(item.ProjectId)) - { - _lowPriorityProcessor.ProcessProjectAsync(analyzers, item, CancellationToken.None).Wait(); - } - } - } - - internal void WaitUntilCompletion() - { - // this shouldn't happen. would like to get some diagnostic - while (_lowPriorityProcessor._workItemQueue.HasAnyWork) - { - FailFast.Fail("How?"); - } - } - } - } - } - } -} -#endif From bd0d760d85c5c7b6d1d0f7ee3f7de6077f324761 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:58:14 -0800 Subject: [PATCH 088/151] simplify --- ...WorkCoordinator.NormalPriorityProcessor.cs | 575 ------------------ 1 file changed, 575 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs deleted file mode 100644 index 703a11618e7b4..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ /dev/null @@ -1,575 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -#if DEBUG -using System.Diagnostics; -#endif - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal sealed partial class SolutionCrawlerRegistrationService -{ - internal sealed partial class WorkCoordinator - { - private sealed partial class IncrementalAnalyzerProcessor - { - private sealed class NormalPriorityProcessor : AbstractPriorityProcessor - { - private readonly AsyncDocumentWorkItemQueue _workItemQueue; - private readonly ConcurrentDictionary _higherPriorityDocumentsNotProcessed; - - private ProjectId? _currentProjectProcessing; - - // this is only used in ResetState to find out solution has changed - // and reset some states such as logging some telemetry or - // priorities active,visible, opened files and etc - private Solution? _lastSolution = null; - - // whether this processor is running or not - private Task _running; - - public NormalPriorityProcessor( - IAsynchronousOperationListener listener, - IncrementalAnalyzerProcessor processor, - Lazy> lazyAnalyzers, - IGlobalOperationNotificationService? globalOperationNotificationService, - TimeSpan backOffTimeSpan, - CancellationToken shutdownToken) - : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) - { - _running = Task.CompletedTask; - _workItemQueue = new AsyncDocumentWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace); - _higherPriorityDocumentsNotProcessed = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 20); - - _currentProjectProcessing = null; - - Start(); - } - - public void Enqueue(WorkItem item) - { - Contract.ThrowIfFalse(item.DocumentId != null, "can only enqueue a document work item"); - - UpdateLastAccessTime(); - - var added = _workItemQueue.AddOrReplace(item); - - Logger.Log(FunctionId.WorkCoordinator_DocumentWorker_Enqueue, s_enqueueLogger, Environment.TickCount, item.DocumentId, !added); - - CheckHigherPriorityDocument(item); - - SolutionCrawlerLogger.LogWorkItemEnqueue( - Processor._logAggregator, item.Language, item.DocumentId, item.InvocationReasons, item.IsLowPriority, item.ActiveMember, added); - } - - private void CheckHigherPriorityDocument(WorkItem item) - { - Contract.ThrowIfFalse(item.DocumentId != null); - - if (!item.InvocationReasons.Contains(PredefinedInvocationReasons.HighPriority)) - { - return; - } - - AddHigherPriorityDocument(item.DocumentId); - } - - private void AddHigherPriorityDocument(DocumentId id) - { - _higherPriorityDocumentsNotProcessed.TryAdd(id, /*unused*/ null); - SolutionCrawlerLogger.LogHigherPriority(Processor._logAggregator, id.Id); - } - - protected override Task WaitAsync(CancellationToken cancellationToken) - => _workItemQueue.WaitAsync(cancellationToken); - - public Task Running => _running; - public int WorkItemCount => _workItemQueue.WorkItemCount; - public bool HasAnyWork => _workItemQueue.HasAnyWork; - - protected override async Task ExecuteAsync() - { - if (CancellationToken.IsCancellationRequested) - { - return; - } - - var source = new TaskCompletionSource(); - try - { - // mark it as running - _running = source.Task; - - await WaitForHigherPriorityOperationsAsync().ConfigureAwait(false); - - // okay, there must be at least one item in the map - await ResetStatesAsync().ConfigureAwait(false); - - if (await TryProcessOneHigherPriorityDocumentAsync().ConfigureAwait(false)) - { - // successfully processed a high priority document. - return; - } - - // process one of documents remaining - if (!_workItemQueue.TryTakeAnyWork( - _currentProjectProcessing, Processor.DependencyGraph, - out var workItem, out var documentCancellation)) - { - return; - } - - // check whether we have been shutdown - if (CancellationToken.IsCancellationRequested) - { - return; - } - - // check whether we have moved to new project - SetProjectProcessing(workItem.ProjectId); - - // process the new document - await ProcessDocumentAsync(Analyzers, workItem, documentCancellation).ConfigureAwait(false); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable(); - } - finally - { - // mark it as done running - source.SetResult(null); - } - } - - protected override Task HigherQueueOperationTask - { - get - { - return Processor._highPriorityProcessor.Running; - } - } - - protected override bool HigherQueueHasWorkItem - { - get - { - return Processor._highPriorityProcessor.HasAnyWork; - } - } - - protected override void OnPaused() - { - base.OnPaused(); - _workItemQueue.RequestCancellationOnRunningTasks(); - } - - private void SetProjectProcessing(ProjectId currentProject) - { - _currentProjectProcessing = currentProject; - } - - private IEnumerable GetPrioritizedPendingDocuments() - { - // First the active document - var activeDocumentId = Processor._documentTracker.TryGetActiveDocument(); - if (activeDocumentId != null) - { - yield return activeDocumentId; - } - - // Now any visible documents - foreach (var visibleDocumentId in Processor._documentTracker.GetVisibleDocuments()) - { - yield return visibleDocumentId; - } - - // Any other high priority documents - foreach (var (documentId, _) in _higherPriorityDocumentsNotProcessed) - { - yield return documentId; - } - } - - private async Task TryProcessOneHigherPriorityDocumentAsync() - { - try - { - if (!Processor._documentTracker.SupportsDocumentTracking) - { - return false; - } - - foreach (var documentId in GetPrioritizedPendingDocuments()) - { - if (CancellationToken.IsCancellationRequested) - { - return true; - } - - // this is a best effort algorithm with some shortcomings. - // - // the most obvious issue is if there is a new work item (without a solution change - but very unlikely) - // for a opened document we already processed, the work item will be treated as a regular one rather than higher priority one - // (opened document) - // see whether we have work item for the document - if (!_workItemQueue.TryTake(documentId, out var workItem, out var documentCancellation)) - { - RemoveHigherPriorityDocument(documentId); - continue; - } - - // okay now we have work to do - await ProcessDocumentAsync(Analyzers, workItem, documentCancellation).ConfigureAwait(false); - - RemoveHigherPriorityDocument(documentId); - return true; - } - - return false; - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable(); - } - } - - private void RemoveHigherPriorityDocument(DocumentId documentId) - { - // remove opened document processed - _higherPriorityDocumentsNotProcessed.TryRemove(documentId, out _); - } - - private async Task ProcessDocumentAsync(ImmutableArray analyzers, WorkItem workItem, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(workItem.DocumentId); - - if (CancellationToken.IsCancellationRequested) - { - return; - } - - var processedEverything = false; - var documentId = workItem.DocumentId; - - // we should always use solution snapshot after workitem is removed from the queue. - // otherwise, we can have a race such as below. - // - // 1.solution crawler picked up a solution - // 2.before processing the solution, an workitem got changed - // 3.and then the work item got picked up from the queue - // 4.and use the work item with the solution that got picked up in step 1 - // - // step 2 is happening because solution has changed, but step 4 used old solution from step 1 - // that doesn't have effects of the solution changes. - // - // solution crawler must remove the work item from the queue first and then pick up the soluton, - // so that the queue gets new work item if there is any solution changes after the work item is removed - // from the queue - // - // using later version of solution is always fine since, as long as there is new work item in the queue, - // solution crawler will eventually call the last workitem with the lastest solution - // making everything to catch up - var solution = Processor._registration.GetSolutionToAnalyze(); - try - { - using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessDocumentAsync, w => w.ToString(), workItem, cancellationToken)) - { - var textDocument = solution.GetTextDocument(documentId) ?? await solution.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); - - if (textDocument != null) - { - // if we are called because a document is opened, we invalidate the document so that - // it can be re-analyzed. otherwise, since newly opened document has same version as - // before analyzer will simply return same data back - if (workItem.MustRefresh) - { - var isOpen = textDocument.IsOpen(); - - await ProcessOpenDocumentIfNeededAsync(analyzers, workItem, textDocument, isOpen, cancellationToken).ConfigureAwait(false); - await ProcessCloseDocumentIfNeededAsync(analyzers, workItem, textDocument, isOpen, cancellationToken).ConfigureAwait(false); - } - - // check whether we are having special reanalyze request - await ProcessReanalyzeDocumentAsync(analyzers, workItem, textDocument, cancellationToken).ConfigureAwait(false); - - await Processor.ProcessDocumentAnalyzersAsync(textDocument, analyzers, workItem, cancellationToken).ConfigureAwait(false); - } - else - { - SolutionCrawlerLogger.LogProcessDocumentNotExist(Processor._logAggregator); - - await RemoveDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); - } - - if (!cancellationToken.IsCancellationRequested) - { - processedEverything = true; - } - } - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - finally - { - // we got cancelled in the middle of processing the document. - // let's make sure newly enqueued work item has all the flag needed. - // Avoid retry attempts after cancellation is requested, since work will not be processed - // after that point. - if (!processedEverything && !CancellationToken.IsCancellationRequested) - { - _workItemQueue.AddOrReplace(workItem.WithAsyncToken(Listener.BeginAsyncOperation("ReenqueueWorkItem"))); - } - - SolutionCrawlerLogger.LogProcessDocument(Processor._logAggregator, documentId.Id, processedEverything); - - // remove one that is finished running - _workItemQueue.MarkWorkItemDoneFor(workItem.DocumentId); - } - } - - private async Task ProcessOpenDocumentIfNeededAsync(ImmutableArray analyzers, WorkItem workItem, TextDocument textDocument, bool isOpen, CancellationToken cancellationToken) - { - if (!isOpen || !workItem.InvocationReasons.Contains(PredefinedInvocationReasons.DocumentOpened)) - { - return; - } - - SolutionCrawlerLogger.LogProcessOpenDocument(Processor._logAggregator, textDocument.Id.Id); - - await Processor.RunAnalyzersAsync(analyzers, textDocument, workItem, DocumentOpenAsync, cancellationToken).ConfigureAwait(false); - return; - - static async Task DocumentOpenAsync(IIncrementalAnalyzer analyzer, TextDocument textDocument, CancellationToken cancellationToken) - { - if (textDocument is Document document) - { - await analyzer.DocumentOpenAsync(document, cancellationToken).ConfigureAwait(false); - } - else - { - await analyzer.NonSourceDocumentOpenAsync(textDocument, cancellationToken).ConfigureAwait(false); - } - } - } - - private async Task ProcessCloseDocumentIfNeededAsync(ImmutableArray analyzers, WorkItem workItem, TextDocument textDocument, bool isOpen, CancellationToken cancellationToken) - { - if (isOpen || !workItem.InvocationReasons.Contains(PredefinedInvocationReasons.DocumentClosed)) - { - return; - } - - SolutionCrawlerLogger.LogProcessCloseDocument(Processor._logAggregator, textDocument.Id.Id); - - await Processor.RunAnalyzersAsync(analyzers, textDocument, workItem, DocumentCloseAsync, cancellationToken).ConfigureAwait(false); - return; - - static async Task DocumentCloseAsync(IIncrementalAnalyzer analyzer, TextDocument textDocument, CancellationToken cancellationToken) - { - if (textDocument is Document document) - { - await analyzer.DocumentCloseAsync(document, cancellationToken).ConfigureAwait(false); - } - else - { - await analyzer.NonSourceDocumentCloseAsync(textDocument, cancellationToken).ConfigureAwait(false); - } - } - } - - private async Task ProcessReanalyzeDocumentAsync(ImmutableArray analyzers, WorkItem workItem, TextDocument document, CancellationToken cancellationToken) - { - try - { - // No-reanalyze request or we already have a request to re-analyze every thing - if (workItem.MustRefresh || !workItem.InvocationReasons.Contains(PredefinedInvocationReasons.Reanalyze)) - { - return; - } - - // First reset the document state in analyzers. - await Processor.RunAnalyzersAsync(analyzers, document, workItem, DocumentResetAsync, cancellationToken).ConfigureAwait(false); - - // No request to re-run syntax change analysis. run it here - var reasons = workItem.InvocationReasons; - if (!reasons.Contains(PredefinedInvocationReasons.SyntaxChanged)) - { - await Processor.RunAnalyzersAsync(analyzers, document, workItem, (a, d, c) => AnalyzeSyntaxAsync(a, d, reasons, c), cancellationToken).ConfigureAwait(false); - } - - // No request to re-run semantic change analysis. run it here - // Note: Semantic analysis is not supported for non-source documents. - if (document is Document sourceDocument && - !workItem.InvocationReasons.Contains(PredefinedInvocationReasons.SemanticChanged)) - { - await Processor.RunAnalyzersAsync(analyzers, sourceDocument, workItem, (a, d, c) => a.AnalyzeDocumentAsync(d, null, reasons, c), cancellationToken).ConfigureAwait(false); - } - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - - return; - - static async Task DocumentResetAsync(IIncrementalAnalyzer analyzer, TextDocument textDocument, CancellationToken cancellationToken) - { - if (textDocument is Document document) - { - await analyzer.DocumentResetAsync(document, cancellationToken).ConfigureAwait(false); - } - else - { - await analyzer.NonSourceDocumentResetAsync(textDocument, cancellationToken).ConfigureAwait(false); - } - } - - static async Task AnalyzeSyntaxAsync(IIncrementalAnalyzer analyzer, TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) - { - if (textDocument is Document document) - { - await analyzer.AnalyzeSyntaxAsync(document, reasons, cancellationToken).ConfigureAwait(false); - } - else - { - await analyzer.AnalyzeNonSourceDocumentAsync(textDocument, reasons, cancellationToken).ConfigureAwait(false); - } - } - } - - private Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) - => RemoveDocumentAsync(Analyzers, documentId, cancellationToken); - - private static async Task RemoveDocumentAsync(ImmutableArray analyzers, DocumentId documentId, CancellationToken cancellationToken) - { - foreach (var analyzer in analyzers) - { - await analyzer.RemoveDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); - } - } - - private async Task ResetStatesAsync() - { - try - { - if (!IsSolutionChanged()) - { - return; - } - - await Processor.RunAnalyzersAsync( - Analyzers, - Processor._registration.GetSolutionToAnalyze(), - workItem: new WorkItem(), (a, s, c) => a.NewSolutionSnapshotAsync(s, c), CancellationToken).ConfigureAwait(false); - - foreach (var id in Processor.GetOpenDocumentIds()) - { - AddHigherPriorityDocument(id); - } - - SolutionCrawlerLogger.LogResetStates(Processor._logAggregator); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e)) - { - throw ExceptionUtilities.Unreachable(); - } - - bool IsSolutionChanged() - { - var currentSolution = Processor._registration.GetSolutionToAnalyze(); - var oldSolution = _lastSolution; - - if (currentSolution == oldSolution) - { - return false; - } - - _lastSolution = currentSolution; - - ResetLogAggregatorIfNeeded(currentSolution, oldSolution); - - return true; - } - - void ResetLogAggregatorIfNeeded(Solution currentSolution, Solution? oldSolution) - { - if (oldSolution == null || currentSolution.Id == oldSolution.Id) - { - // we log aggregated info when solution is changed such as - // new solution is opened or solution is closed - return; - } - - // this log things like how many time we analyzed active files, how many times other files are analyzed, - // avg time to analyze files, how many solution snapshot got analyzed and etc. - // all accumultation is done in VS side and we only send statistics to VS telemetry otherwise, it is too much - // data to send - SolutionCrawlerLogger.LogIncrementalAnalyzerProcessorStatistics( - Processor._registration.CorrelationId, oldSolution, Processor._logAggregator, Analyzers); - - Processor.ResetLogAggregator(); - } - } - - public override void Shutdown() - { - base.Shutdown(); - - _workItemQueue.Dispose(); - } - - internal TestAccessor GetTestAccessor() - { - return new TestAccessor(this); - } - - internal readonly struct TestAccessor - { - private readonly NormalPriorityProcessor _normalPriorityProcessor; - - internal TestAccessor(NormalPriorityProcessor normalPriorityProcessor) - { - _normalPriorityProcessor = normalPriorityProcessor; - } - - internal void WaitUntilCompletion(ImmutableArray analyzers, List items) - { - foreach (var item in items) - { - _normalPriorityProcessor.ProcessDocumentAsync(analyzers, item, CancellationToken.None).Wait(); - } - } - - internal void WaitUntilCompletion() - { - // this shouldn't happen. would like to get some diagnostic - while (_normalPriorityProcessor._workItemQueue.HasAnyWork) - { - FailFast.Fail("How?"); - } - } - } - } - } - } -} -#endif From a3539047e72106c9cd971d10f06c25a9d0b903ae Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:58:20 -0800 Subject: [PATCH 089/151] simplify --- ...WorkCoordinator.SemanticChangeProcessor.cs | 450 ------------------ 1 file changed, 450 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs deleted file mode 100644 index 8fd5fc2d8a180..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs +++ /dev/null @@ -1,450 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal sealed partial class SolutionCrawlerRegistrationService -{ - /// - /// this will be used in the unit test to indicate certain action has happened or not. - /// - public const string EnqueueItem = nameof(EnqueueItem); - - internal sealed partial class WorkCoordinator - { - private sealed class SemanticChangeProcessor : IdleProcessor - { - private static readonly Func s_enqueueLogger = (tick, documentId, hint) => $"Tick:{tick}, {documentId}, {documentId.ProjectId}, hint:{hint}"; - - private readonly SemaphoreSlim _gate; - - private readonly Registration _registration; - private readonly ProjectProcessor _processor; - - private readonly NonReentrantLock _workGate = new(); - private readonly Dictionary _pendingWork = []; - - public SemanticChangeProcessor( - IAsynchronousOperationListener listener, - Registration registration, - IncrementalAnalyzerProcessor documentWorkerProcessor, - TimeSpan backOffTimeSpan, - TimeSpan projectBackOffTimeSpan, - CancellationToken cancellationToken) - : base(listener, backOffTimeSpan, cancellationToken) - { - _gate = new SemaphoreSlim(initialCount: 0); - - _registration = registration; - - _processor = new ProjectProcessor(listener, registration, documentWorkerProcessor, projectBackOffTimeSpan, cancellationToken); - - Start(); - - // Register a clean-up task to ensure pending work items are flushed from the queue if they will - // never be processed. - AsyncProcessorTask.ContinueWith( - _ => ClearQueueWorker(_workGate, _pendingWork, data => data.AsyncToken), - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - - protected override void OnPaused() - { - } - - public override Task AsyncProcessorTask - => Task.WhenAll(base.AsyncProcessorTask, _processor.AsyncProcessorTask); - - protected override Task WaitAsync(CancellationToken cancellationToken) - => _gate.WaitAsync(cancellationToken); - - protected override async Task ExecuteAsync() - { - var data = Dequeue(); - - using (data.AsyncToken) - { - // we have a hint. check whether we can take advantage of it - if (await TryEnqueueFromHintAsync(data).ConfigureAwait(continueOnCapturedContext: false)) - return; - - EnqueueFullProjectDependency(data.Project); - } - } - - private Data Dequeue() - => DequeueWorker(_workGate, _pendingWork, CancellationToken); - - private async Task TryEnqueueFromHintAsync(Data data) - { - var changedMember = data.ChangedMember; - if (changedMember == null) - return false; - - var document = data.GetRequiredDocument(); - - // see whether we already have semantic model. otherwise, use the expansive full project dependency one - // TODO: if there is a reliable way to track changed member, we could use GetSemanticModel here which could - // rebuild compilation from scratch - if (!document.TryGetSemanticModel(out var model) || - !changedMember.TryResolve(await document.GetSyntaxRootAsync(CancellationToken).ConfigureAwait(false), out SyntaxNode? declarationNode)) - { - return false; - } - - var symbol = model.GetDeclaredSymbol(declarationNode, CancellationToken); - if (symbol == null) - { - return false; - } - - return await TryEnqueueFromMemberAsync(document, symbol).ConfigureAwait(false) || - await TryEnqueueFromTypeAsync(document, symbol).ConfigureAwait(false); - } - - private async Task TryEnqueueFromTypeAsync(Document document, ISymbol symbol) - { - if (!IsType(symbol)) - { - return false; - } - - if (symbol.DeclaredAccessibility == Accessibility.Private) - { - await EnqueueWorkItemAsync(document, symbol).ConfigureAwait(false); - - Logger.Log(FunctionId.WorkCoordinator_SemanticChange_EnqueueFromType, symbol.Name); - return true; - } - - if (IsInternal(symbol)) - { - var assembly = symbol.ContainingAssembly; - EnqueueFullProjectDependency(document.Project, assembly); - return true; - } - - return false; - } - - private async Task TryEnqueueFromMemberAsync(Document document, ISymbol symbol) - { - if (!IsMember(symbol)) - { - return false; - } - - var typeSymbol = symbol.ContainingType; - - if (symbol.DeclaredAccessibility == Accessibility.Private) - { - await EnqueueWorkItemAsync(document, symbol).ConfigureAwait(false); - - Logger.Log(FunctionId.WorkCoordinator_SemanticChange_EnqueueFromMember, symbol.Name); - return true; - } - - if (typeSymbol == null) - { - return false; - } - - return await TryEnqueueFromTypeAsync(document, typeSymbol).ConfigureAwait(false); - } - - private Task EnqueueWorkItemAsync(Document document, ISymbol symbol) - => EnqueueWorkItemAsync(document, symbol.ContainingType != null ? symbol.ContainingType.Locations : symbol.Locations); - - private async Task EnqueueWorkItemAsync(Document thisDocument, ImmutableArray locations) - { - var solution = thisDocument.Project.Solution; - var projectId = thisDocument.Id.ProjectId; - - foreach (var location in locations) - { - Debug.Assert(location.IsInSource); - - var documentId = solution.GetDocumentId(location.SourceTree, projectId); - if (documentId == null || thisDocument.Id == documentId) - continue; - - await _processor.EnqueueWorkItemAsync(solution.GetRequiredProject(documentId.ProjectId), documentId, document: null).ConfigureAwait(false); - } - } - - private static bool IsInternal(ISymbol symbol) - { - return symbol.DeclaredAccessibility is Accessibility.Internal or - Accessibility.ProtectedAndInternal or - Accessibility.ProtectedOrInternal; - } - - private static bool IsType(ISymbol symbol) - => symbol.Kind == SymbolKind.NamedType; - - private static bool IsMember(ISymbol symbol) - { - return symbol.Kind is SymbolKind.Event or - SymbolKind.Field or - SymbolKind.Method or - SymbolKind.Property; - } - - private void EnqueueFullProjectDependency(Project project, IAssemblySymbol? internalVisibleToAssembly = null) - { - var self = project.Id; - - // if there is no hint (this can happen for cases such as solution/project load and etc), - // we can postpone it even further - if (internalVisibleToAssembly == null) - { - _processor.Enqueue(self, needDependencyTracking: true); - return; - } - - // most likely we got here since we are called due to typing. - // calculate dependency here and register each affected project to the next pipe line - var solution = project.Solution; - foreach (var projectId in GetProjectsToAnalyze(solution, self)) - { - var otherProject = solution.GetProject(projectId); - if (otherProject == null) - continue; - - if (otherProject.TryGetCompilation(out var compilation)) - { - var assembly = compilation.Assembly; - if (assembly != null && !assembly.IsSameAssemblyOrHasFriendAccessTo(internalVisibleToAssembly)) - continue; - } - - _processor.Enqueue(projectId); - } - - Logger.Log(FunctionId.WorkCoordinator_SemanticChange_FullProjects, internalVisibleToAssembly == null ? "full" : "internals"); - } - - public void Enqueue(Project project, DocumentId documentId, Document? document, SyntaxPath? changedMember) - { - UpdateLastAccessTime(); - - using (_workGate.DisposableWait(CancellationToken)) - { - if (_pendingWork.TryGetValue(documentId, out var data)) - { - // create new async token and dispose old one. - var newAsyncToken = Listener.BeginAsyncOperation(nameof(Enqueue), tag: _registration.Workspace); - data.AsyncToken.Dispose(); - - _pendingWork[documentId] = new Data(project, documentId, document, data.ChangedMember == changedMember ? changedMember : null, newAsyncToken); - return; - } - - _pendingWork.Add(documentId, new Data(project, documentId, document, changedMember, Listener.BeginAsyncOperation(nameof(Enqueue), tag: _registration.Workspace))); - _gate.Release(); - } - - Logger.Log(FunctionId.WorkCoordinator_SemanticChange_Enqueue, s_enqueueLogger, Environment.TickCount, documentId, changedMember != null); - } - - private static TValue DequeueWorker(NonReentrantLock gate, Dictionary map, CancellationToken cancellationToken) - where TKey : notnull - { - using (gate.DisposableWait(cancellationToken)) - { - var first = default(KeyValuePair); - foreach (var kv in map) - { - first = kv; - break; - } - - // this is only one that removes data from the queue. so, it should always succeed - var result = map.Remove(first.Key); - Debug.Assert(result); - - return first.Value; - } - } - - private static void ClearQueueWorker(NonReentrantLock gate, Dictionary map, Func disposerSelector) - where TKey : notnull - { - using (gate.DisposableWait(CancellationToken.None)) - { - foreach (var (_, data) in map) - { - disposerSelector?.Invoke(data)?.Dispose(); - } - - map.Clear(); - } - } - - private static IEnumerable GetProjectsToAnalyze(Solution solution, ProjectId projectId) - { - var graph = solution.GetProjectDependencyGraph(); - - // Reanalyze direct dependencies only as reanalyzing all transitive dependencies is very expensive. - return graph.GetProjectsThatDirectlyDependOnThisProject(projectId).Concat(projectId); - } - - private readonly struct Data(Project project, DocumentId documentId, Document? document, SyntaxPath? changedMember, IAsyncToken asyncToken) - { - private readonly DocumentId _documentId = documentId; - private readonly Document? _document = document; - - public readonly Project Project = project; - public readonly SyntaxPath? ChangedMember = changedMember; - public readonly IAsyncToken AsyncToken = asyncToken; - - public Document GetRequiredDocument() - => WorkCoordinator.GetRequiredDocument(Project, _documentId, _document); - } - - private class ProjectProcessor : IdleProcessor - { - private static readonly Func s_enqueueLogger = (t, i) => string.Format("[{0}] {1}", t, i.ToString()); - - private readonly SemaphoreSlim _gate; - - private readonly Registration _registration; - private readonly IncrementalAnalyzerProcessor _processor; - - private readonly NonReentrantLock _workGate = new(); - private readonly Dictionary _pendingWork = []; - - public ProjectProcessor( - IAsynchronousOperationListener listener, - Registration registration, - IncrementalAnalyzerProcessor processor, - TimeSpan backOffTimeSpan, - CancellationToken cancellationToken) - : base(listener, backOffTimeSpan, cancellationToken) - { - _registration = registration; - _processor = processor; - - _gate = new SemaphoreSlim(initialCount: 0); - - Start(); - - // Register a clean-up task to ensure pending work items are flushed from the queue if they will - // never be processed. - AsyncProcessorTask.ContinueWith( - _ => ClearQueueWorker(_workGate, _pendingWork, data => data.AsyncToken), - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - - protected override void OnPaused() - { - } - - public void Enqueue(ProjectId projectId, bool needDependencyTracking = false) - { - UpdateLastAccessTime(); - - using (_workGate.DisposableWait(CancellationToken)) - { - // the project is already in the queue. nothing needs to be done - if (_pendingWork.ContainsKey(projectId)) - { - return; - } - - var data = new Data(projectId, needDependencyTracking, Listener.BeginAsyncOperation(nameof(Enqueue), tag: _registration.Workspace)); - - _pendingWork.Add(projectId, data); - _gate.Release(); - } - - Logger.Log(FunctionId.WorkCoordinator_Project_Enqueue, s_enqueueLogger, Environment.TickCount, projectId); - } - - public async Task EnqueueWorkItemAsync(Project project, DocumentId documentId, Document? document) - { - // we are shutting down - CancellationToken.ThrowIfCancellationRequested(); - - // call to this method is serialized. and only this method does the writing. - var priorityService = project.GetLanguageService(); - var isLowPriority = priorityService != null && await priorityService.IsLowPriorityAsync(GetRequiredDocument(project, documentId, document), CancellationToken).ConfigureAwait(false); - - _processor.Enqueue( - new WorkItem(documentId, project.Language, InvocationReasons.SemanticChanged, - isLowPriority, activeMember: null, Listener.BeginAsyncOperation(nameof(EnqueueWorkItemAsync), tag: EnqueueItem))); - } - - protected override Task WaitAsync(CancellationToken cancellationToken) - => _gate.WaitAsync(cancellationToken); - - protected override async Task ExecuteAsync() - { - var data = Dequeue(); - - using (data.AsyncToken) - { - var project = _registration.GetSolutionToAnalyze().GetProject(data.ProjectId); - if (project == null) - { - return; - } - - if (!data.NeedDependencyTracking) - { - await EnqueueWorkItemAsync(project).ConfigureAwait(false); - return; - } - - // do dependency tracking here with current solution - var solution = _registration.GetSolutionToAnalyze(); - foreach (var projectId in GetProjectsToAnalyze(solution, data.ProjectId)) - { - project = solution.GetProject(projectId); - await EnqueueWorkItemAsync(project).ConfigureAwait(false); - } - } - } - - private Data Dequeue() - => DequeueWorker(_workGate, _pendingWork, CancellationToken); - - private async Task EnqueueWorkItemAsync(Project? project) - { - if (project == null) - return; - - foreach (var documentId in project.DocumentIds) - await EnqueueWorkItemAsync(project, documentId, document: null).ConfigureAwait(false); - } - - private readonly struct Data(ProjectId projectId, bool needDependencyTracking, IAsyncToken asyncToken) - { - public readonly IAsyncToken AsyncToken = asyncToken; - public readonly ProjectId ProjectId = projectId; - public readonly bool NeedDependencyTracking = needDependencyTracking; - } - } - } - } -} -#endif From 7f5ce9fdfb4f0aa736592722bbd34ea5d099064b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:58:26 -0800 Subject: [PATCH 090/151] simplify --- .../WorkCoordinator.WorkItem.cs | 157 ------------------ 1 file changed, 157 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs deleted file mode 100644 index 000332d35a26b..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.WorkItem.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService -{ - internal partial class WorkCoordinator - { - // this is internal only type - internal readonly struct WorkItem - { - // project related workitem - public readonly ProjectId ProjectId; - - // document related workitem - public readonly DocumentId? DocumentId; - public readonly string Language; - public readonly InvocationReasons InvocationReasons; - public readonly bool IsLowPriority; - - // extra info - public readonly SyntaxPath? ActiveMember; - - /// - /// Non-empty if this work item is intended to be executed only for specific incremental analyzer(s). - /// Otherwise, the work item is applicable to all relevant incremental analyzers. - /// - public readonly ImmutableHashSet SpecificAnalyzers; - - /// - /// Gets all the applicable analyzers to execute for this work item. - /// If this work item has any specific analyzer(s), then returns the intersection of - /// and the given . - /// Otherwise, returns . - /// - public IEnumerable GetApplicableAnalyzers(ImmutableArray allAnalyzers) - => SpecificAnalyzers?.Count > 0 ? SpecificAnalyzers.Where(allAnalyzers.Contains) : allAnalyzers; - - // common - public readonly IAsyncToken AsyncToken; - - public bool MustRefresh - { - get - { - // in current design, we need to re-run all incremental analyzer on document open and close - // so that incremental analyzer who only cares about opened document can have a chance to clean up - // its state. - return InvocationReasons.Contains(PredefinedInvocationReasons.DocumentOpened) || - InvocationReasons.Contains(PredefinedInvocationReasons.DocumentClosed); - } - } - - private WorkItem( - DocumentId? documentId, - ProjectId projectId, - string language, - InvocationReasons invocationReasons, - bool isLowPriority, - SyntaxPath? activeMember, - ImmutableHashSet specificAnalyzers, - IAsyncToken asyncToken) - { - Debug.Assert(documentId == null || documentId.ProjectId == projectId); - - DocumentId = documentId; - ProjectId = projectId; - Language = language; - InvocationReasons = invocationReasons; - IsLowPriority = isLowPriority; - - ActiveMember = activeMember; - SpecificAnalyzers = specificAnalyzers; - - AsyncToken = asyncToken; - } - - public WorkItem(DocumentId documentId, string language, InvocationReasons invocationReasons, bool isLowPriority, SyntaxPath? activeMember, IAsyncToken asyncToken) - : this(documentId, documentId.ProjectId, language, invocationReasons, isLowPriority, activeMember, [], asyncToken) - { - } - - public WorkItem(DocumentId documentId, string language, InvocationReasons invocationReasons, bool isLowPriority, IIncrementalAnalyzer? analyzer, IAsyncToken asyncToken) - : this(documentId, documentId.ProjectId, language, invocationReasons, isLowPriority, activeMember: null, - analyzer == null ? [] : [analyzer], - asyncToken) - { - } - - public object Key => DocumentId ?? (object)ProjectId; - - public WorkItem With( - InvocationReasons invocationReasons, - SyntaxPath? currentMember, - ImmutableHashSet specificAnalyzers, - IAsyncToken asyncToken) - { - // dispose old one - AsyncToken.Dispose(); - - // create new work item - return new WorkItem( - DocumentId, ProjectId, Language, - InvocationReasons.With(invocationReasons), - IsLowPriority, - ActiveMember == currentMember ? currentMember : null, - ComputeNewSpecificAnalyzers(specificAnalyzers, SpecificAnalyzers), - asyncToken); - - static ImmutableHashSet ComputeNewSpecificAnalyzers(ImmutableHashSet specificAnalyzers1, ImmutableHashSet specificAnalyzers2) - { - // An empty analyzer list means run all analyzers, so empty always wins over any specific - if (specificAnalyzers1.IsEmpty || specificAnalyzers2.IsEmpty) - { - return []; - } - - // Otherwise, if both sets have analyzers we use a union of the two - return specificAnalyzers1.Union(specificAnalyzers2); - } - } - - public WorkItem WithAsyncToken(IAsyncToken asyncToken) - => new(DocumentId, ProjectId, Language, InvocationReasons, IsLowPriority, ActiveMember, SpecificAnalyzers, asyncToken); - - public WorkItem ToProjectWorkItem(IAsyncToken asyncToken) - { - RoslynDebug.Assert(DocumentId != null); - - // create new work item that represents work per project - return new WorkItem( - documentId: null, - DocumentId.ProjectId, - Language, - InvocationReasons, - IsLowPriority, - ActiveMember, - SpecificAnalyzers, - asyncToken); - } - - public override string ToString() - => $"{DocumentId?.ToString() ?? ProjectId.ToString()}, ({InvocationReasons}), LowPriority:{IsLowPriority}, ActiveMember:{ActiveMember != null}, ({string.Join("|", SpecificAnalyzers.Select(a => a.GetType().Name))})"; - } - } -} -#endif From 5c178fc32a9b69279e96c1a8ddc820c1b37c2a58 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 11:58:32 -0800 Subject: [PATCH 091/151] simplify --- .../SolutionCrawler/WorkCoordinator.cs | 764 ------------------ 1 file changed, 764 deletions(-) delete mode 100644 src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs deleted file mode 100644 index 9bb6d799ebe4d..0000000000000 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ /dev/null @@ -1,764 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal partial class SolutionCrawlerRegistrationService -{ - internal sealed partial class WorkCoordinator - { - private readonly Registration _registration; - private readonly object _gate = new(); - - private readonly CountLogAggregator _logAggregator = new(); - private readonly IAsynchronousOperationListener _listener; - private readonly IDocumentTrackingService _documentTrackingService; - private readonly ISolutionCrawlerOptionsService? _solutionCrawlerOptions; - - private readonly CancellationTokenSource _shutdownNotificationSource = new(); - private readonly CancellationToken _shutdownToken; - private readonly TaskQueue _eventProcessingQueue; - - // points to processor task - private readonly IncrementalAnalyzerProcessor _documentAndProjectWorkerProcessor; - private readonly SemanticChangeProcessor _semanticChangeProcessor; - - public WorkCoordinator( - IAsynchronousOperationListener listener, - IEnumerable> analyzerProviders, - bool initializeLazily, - Registration registration) - { - _registration = registration; - - _listener = listener; - _documentTrackingService = _registration.Workspace.Services.GetRequiredService(); - _solutionCrawlerOptions = _registration.Workspace.Services.GetService(); - - // event and worker queues - _shutdownToken = _shutdownNotificationSource.Token; - - _eventProcessingQueue = new TaskQueue(listener, TaskScheduler.Default); - - var activeFileBackOffTimeSpan = SolutionCrawlerTimeSpan.ActiveFileWorkerBackOff; - var allFilesWorkerBackOffTimeSpan = SolutionCrawlerTimeSpan.AllFilesWorkerBackOff; - var entireProjectWorkerBackOffTimeSpan = SolutionCrawlerTimeSpan.EntireProjectWorkerBackOff; - - _documentAndProjectWorkerProcessor = new IncrementalAnalyzerProcessor( - listener, analyzerProviders, initializeLazily, _registration, - activeFileBackOffTimeSpan, allFilesWorkerBackOffTimeSpan, entireProjectWorkerBackOffTimeSpan, _shutdownToken); - - var semanticBackOffTimeSpan = SolutionCrawlerTimeSpan.SemanticChangeBackOff; - var projectBackOffTimeSpan = SolutionCrawlerTimeSpan.ProjectPropagationBackOff; - - _semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpan, projectBackOffTimeSpan, _shutdownToken); - - _registration.Workspace.WorkspaceChanged += OnWorkspaceChanged; - _registration.Workspace.TextDocumentOpened += OnTextDocumentOpened; - _registration.Workspace.TextDocumentClosed += OnTextDocumentClosed; - - // subscribe to active document changed event for active file background analysis scope. - _documentTrackingService.ActiveDocumentChanged += OnActiveDocumentSwitched; - } - - public int CorrelationId => _registration.CorrelationId; - - public void AddAnalyzer(IIncrementalAnalyzer analyzer, bool highPriorityForActiveFile) - { - // add analyzer - _documentAndProjectWorkerProcessor.AddAnalyzer(analyzer, highPriorityForActiveFile); - - // and ask to re-analyze whole solution for the given analyzer - var scope = new ReanalyzeScope(_registration.GetSolutionToAnalyze().Id); - Reanalyze(analyzer, scope); - } - - public void Shutdown(bool blockingShutdown) - { - _documentTrackingService.ActiveDocumentChanged -= OnActiveDocumentSwitched; - - // detach from the workspace - _registration.Workspace.WorkspaceChanged -= OnWorkspaceChanged; - _registration.Workspace.TextDocumentOpened -= OnTextDocumentOpened; - _registration.Workspace.TextDocumentClosed -= OnTextDocumentClosed; - - // cancel any pending blocks - _shutdownNotificationSource.Cancel(); - - _documentAndProjectWorkerProcessor.Shutdown(); - - SolutionCrawlerLogger.LogWorkCoordinatorShutdown(CorrelationId, _logAggregator); - - if (blockingShutdown) - { - var shutdownTask = Task.WhenAll( - _eventProcessingQueue.LastScheduledTask, - _documentAndProjectWorkerProcessor.AsyncProcessorTask, - _semanticChangeProcessor.AsyncProcessorTask); - - try - { - shutdownTask.Wait(TimeSpan.FromSeconds(5)); - } - catch (AggregateException ex) - { - ex.Handle(e => e is OperationCanceledException); - } - - if (!shutdownTask.IsCompleted) - { - SolutionCrawlerLogger.LogWorkCoordinatorShutdownTimeout(CorrelationId); - } - } - - foreach (var analyzer in _documentAndProjectWorkerProcessor.Analyzers) - { - (analyzer as IDisposable)?.Dispose(); - } - } - - public void Reanalyze(IIncrementalAnalyzer analyzer, ReanalyzeScope scope, bool highPriority = false) - { - _eventProcessingQueue.ScheduleTask("Reanalyze", - () => EnqueueWorkItemAsync(analyzer, scope, highPriority), _shutdownToken); - - if (scope.HasMultipleDocuments) - { - // log big reanalysis request from things like fix all, suppress all or option changes - // we are not interested in 1 file re-analysis request which can happen from like venus typing - var solution = _registration.GetSolutionToAnalyze(); - SolutionCrawlerLogger.LogReanalyze( - CorrelationId, analyzer, scope.GetDocumentCount(solution), scope.GetLanguagesStringForTelemetry(solution), highPriority); - } - } - - private void OnActiveDocumentSwitched(object? sender, DocumentId? activeDocumentId) - { - if (activeDocumentId == null) - return; - - var solution = _registration.GetSolutionToAnalyze(); - EnqueueFullDocumentEvent(solution, activeDocumentId, InvocationReasons.ActiveDocumentSwitched, eventName: nameof(OnActiveDocumentSwitched)); - } - - private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs args) - { - // guard us from cancellation - try - { - ProcessEvent(args, "OnWorkspaceChanged"); - } - catch (OperationCanceledException oce) - { - if (NotOurShutdownToken(oce)) - { - throw; - } - - // it is our cancellation, ignore - } - catch (AggregateException ae) - { - ae = ae.Flatten(); - - // If we had a mix of exceptions, don't eat it - if (ae.InnerExceptions.Any(e => e is not OperationCanceledException) || - ae.InnerExceptions.Cast().Any(NotOurShutdownToken)) - { - // We had a cancellation with a different token, so don't eat it - throw; - } - - // it is our cancellation, ignore - } - } - - private bool NotOurShutdownToken(OperationCanceledException oce) - => oce.CancellationToken == _shutdownToken; - - private void ProcessEvent(WorkspaceChangeEventArgs args, string eventName) - { - SolutionCrawlerLogger.LogWorkspaceEvent(_logAggregator, args.Kind); - - // TODO: add telemetry that record how much it takes to process an event (max, min, average and etc) - switch (args.Kind) - { - case WorkspaceChangeKind.SolutionAdded: - EnqueueFullSolutionEvent(args.NewSolution, InvocationReasons.DocumentAdded, eventName); - break; - - case WorkspaceChangeKind.SolutionRemoved: - case WorkspaceChangeKind.SolutionCleared: - EnqueueFullSolutionEvent(args.OldSolution, InvocationReasons.SolutionRemoved, eventName); - break; - - case WorkspaceChangeKind.SolutionChanged: - case WorkspaceChangeKind.SolutionReloaded: - EnqueueSolutionChangedEvent(args.OldSolution, args.NewSolution, eventName); - break; - - case WorkspaceChangeKind.ProjectAdded: - Contract.ThrowIfNull(args.ProjectId); - EnqueueFullProjectEvent(args.NewSolution, args.ProjectId, InvocationReasons.DocumentAdded, eventName); - break; - - case WorkspaceChangeKind.ProjectRemoved: - Contract.ThrowIfNull(args.ProjectId); - EnqueueFullProjectEvent(args.OldSolution, args.ProjectId, InvocationReasons.DocumentRemoved, eventName); - break; - - case WorkspaceChangeKind.ProjectChanged: - case WorkspaceChangeKind.ProjectReloaded: - Contract.ThrowIfNull(args.ProjectId); - EnqueueProjectChangedEvent(args.OldSolution, args.NewSolution, args.ProjectId, eventName); - break; - - case WorkspaceChangeKind.DocumentAdded: - Contract.ThrowIfNull(args.DocumentId); - EnqueueFullDocumentEvent(args.NewSolution, args.DocumentId, InvocationReasons.DocumentAdded, eventName); - break; - - case WorkspaceChangeKind.DocumentRemoved: - Contract.ThrowIfNull(args.DocumentId); - EnqueueFullDocumentEvent(args.OldSolution, args.DocumentId, InvocationReasons.DocumentRemoved, eventName); - break; - - case WorkspaceChangeKind.DocumentChanged: - case WorkspaceChangeKind.DocumentReloaded: - Contract.ThrowIfNull(args.DocumentId); - EnqueueDocumentChangedEvent(args.OldSolution, args.NewSolution, args.DocumentId, eventName); - break; - - case WorkspaceChangeKind.AdditionalDocumentAdded: - case WorkspaceChangeKind.AdditionalDocumentRemoved: - case WorkspaceChangeKind.AdditionalDocumentChanged: - case WorkspaceChangeKind.AdditionalDocumentReloaded: - case WorkspaceChangeKind.AnalyzerConfigDocumentAdded: - case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: - case WorkspaceChangeKind.AnalyzerConfigDocumentChanged: - case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: - // If an additional file or .editorconfig has changed we need to reanalyze the entire project. - Contract.ThrowIfNull(args.ProjectId); - EnqueueFullProjectEvent(args.NewSolution, args.ProjectId, InvocationReasons.AdditionalDocumentChanged, eventName); - break; - - default: - throw ExceptionUtilities.UnexpectedValue(args.Kind); - } - } - - private void OnTextDocumentOpened(object? sender, TextDocumentEventArgs e) - { - _eventProcessingQueue.ScheduleTask("OnTextDocumentOpened", - () => EnqueueDocumentWorkItemAsync(e.Document.Project, e.Document.Id, e.Document, InvocationReasons.DocumentOpened), _shutdownToken); - } - - private void OnTextDocumentClosed(object? sender, TextDocumentEventArgs e) - { - _eventProcessingQueue.ScheduleTask("OnTextDocumentClosed", - () => EnqueueDocumentWorkItemAsync(e.Document.Project, e.Document.Id, e.Document, InvocationReasons.DocumentClosed), _shutdownToken); - } - - private void EnqueueSolutionChangedEvent(Solution oldSolution, Solution newSolution, string eventName) - { - _eventProcessingQueue.ScheduleTask( - eventName, - async () => - { - var solutionChanges = newSolution.GetChanges(oldSolution); - - // TODO: Async version for GetXXX methods? - foreach (var addedProject in solutionChanges.GetAddedProjects()) - { - await EnqueueFullProjectWorkItemAsync(addedProject, InvocationReasons.DocumentAdded).ConfigureAwait(false); - } - - foreach (var projectChanges in solutionChanges.GetProjectChanges()) - { - await EnqueueWorkItemAsync(projectChanges).ConfigureAwait(continueOnCapturedContext: false); - } - - foreach (var removedProject in solutionChanges.GetRemovedProjects()) - { - await EnqueueFullProjectWorkItemAsync(removedProject, InvocationReasons.DocumentRemoved).ConfigureAwait(false); - } - }, - _shutdownToken); - } - - private void EnqueueFullSolutionEvent(Solution solution, InvocationReasons invocationReasons, string eventName) - { - _eventProcessingQueue.ScheduleTask( - eventName, - async () => - { - foreach (var projectId in solution.ProjectIds) - { - await EnqueueFullProjectWorkItemAsync(solution.GetRequiredProject(projectId), invocationReasons).ConfigureAwait(false); - } - }, - _shutdownToken); - } - - private void EnqueueProjectChangedEvent(Solution oldSolution, Solution newSolution, ProjectId projectId, string eventName) - { - _eventProcessingQueue.ScheduleTask( - eventName, - async () => - { - var oldProject = oldSolution.GetRequiredProject(projectId); - var newProject = newSolution.GetRequiredProject(projectId); - - await EnqueueWorkItemAsync(newProject.GetChanges(oldProject)).ConfigureAwait(false); - }, - _shutdownToken); - } - - private void EnqueueFullProjectEvent(Solution solution, ProjectId projectId, InvocationReasons invocationReasons, string eventName) - { - _eventProcessingQueue.ScheduleTask(eventName, - () => EnqueueFullProjectWorkItemAsync(solution.GetRequiredProject(projectId), invocationReasons), _shutdownToken); - } - - private void EnqueueFullDocumentEvent(Solution solution, DocumentId documentId, InvocationReasons invocationReasons, string eventName) - { - _eventProcessingQueue.ScheduleTask( - eventName, - () => - { - var project = solution.GetRequiredProject(documentId.ProjectId); - return EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons); - }, - _shutdownToken); - } - - private void EnqueueDocumentChangedEvent(Solution oldSolution, Solution newSolution, DocumentId documentId, string eventName) - { - // document changed event is the special one. - _eventProcessingQueue.ScheduleTask( - eventName, - async () => - { - var oldProject = oldSolution.GetRequiredProject(documentId.ProjectId); - var newProject = newSolution.GetRequiredProject(documentId.ProjectId); - - await EnqueueChangedDocumentWorkItemAsync(oldProject.GetRequiredDocument(documentId), newProject.GetRequiredDocument(documentId)).ConfigureAwait(false); - - // If all features are enabled for source generated documents, the solution crawler needs to - // include them in incremental analysis. - if (_solutionCrawlerOptions?.EnableDiagnosticsInSourceGeneratedFiles == true) - { - // TODO: if this becomes a hot spot, we should be able to expose/access the dictionary - // underneath GetSourceGeneratedDocumentsAsync rather than create a new one here. - var oldProjectSourceGeneratedDocuments = await oldProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); - var oldProjectSourceGeneratedDocumentsById = oldProjectSourceGeneratedDocuments.ToDictionary(static document => document.Id); - var newProjectSourceGeneratedDocuments = await newProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); - var newProjectSourceGeneratedDocumentsById = newProjectSourceGeneratedDocuments.ToDictionary(static document => document.Id); - - foreach (var (oldDocumentId, _) in oldProjectSourceGeneratedDocumentsById) - { - if (!newProjectSourceGeneratedDocumentsById.ContainsKey(oldDocumentId)) - { - // This source generated document was removed - EnqueueFullDocumentEvent(oldSolution, oldDocumentId, InvocationReasons.DocumentRemoved, "OnWorkspaceChanged"); - } - } - - foreach (var (newDocumentId, newDocument) in newProjectSourceGeneratedDocumentsById) - { - if (!oldProjectSourceGeneratedDocumentsById.TryGetValue(newDocumentId, out var oldDocument)) - { - // This source generated document was added - EnqueueFullDocumentEvent(newSolution, newDocumentId, InvocationReasons.DocumentAdded, "OnWorkspaceChanged"); - } - else - { - // This source generated document may have changed - await EnqueueChangedDocumentWorkItemAsync(oldDocument, newDocument).ConfigureAwait(continueOnCapturedContext: false); - } - } - } - }, - _shutdownToken); - } - - private async Task EnqueueDocumentWorkItemAsync(Project project, DocumentId documentId, TextDocument? document, InvocationReasons invocationReasons, SyntaxNode? changedMember = null) - { - // we are shutting down - _shutdownToken.ThrowIfCancellationRequested(); - - var priorityService = project.GetLanguageService(); - document ??= project.GetTextDocument(documentId); - var sourceDocument = document as Document; - var isLowPriority = priorityService != null && sourceDocument != null && await priorityService.IsLowPriorityAsync(sourceDocument, _shutdownToken).ConfigureAwait(false); - - var currentMember = GetSyntaxPath(changedMember); - - // call to this method is serialized. and only this method does the writing. - _documentAndProjectWorkerProcessor.Enqueue( - new WorkItem(documentId, project.Language, invocationReasons, isLowPriority, currentMember, _listener.BeginAsyncOperation("WorkItem"))); - - // enqueue semantic work planner - if (invocationReasons.Contains(PredefinedInvocationReasons.SemanticChanged) && sourceDocument != null) - { - // must use "Document" here so that the snapshot doesn't go away. we need the snapshot to calculate p2p dependency graph later. - // due to this, we might hold onto solution (and things kept alive by it) little bit longer than usual. - _semanticChangeProcessor.Enqueue(project, documentId, sourceDocument, currentMember); - } - } - - private static Document GetRequiredDocument(Project project, DocumentId documentId, Document? document) - => document ?? project.GetRequiredDocument(documentId); - - private static SyntaxPath? GetSyntaxPath(SyntaxNode? changedMember) - { - // using syntax path might be too expansive since it will be created on every keystroke. - // but currently, we have no other way to track a node between two different tree (even for incrementally parsed one) - if (changedMember == null) - { - return null; - } - - return new SyntaxPath(changedMember); - } - - private async Task EnqueueFullProjectWorkItemAsync(Project project, InvocationReasons invocationReasons) - { - foreach (var documentId in project.DocumentIds) - await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); - - foreach (var documentId in project.AdditionalDocumentIds) - await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); - - foreach (var documentId in project.AnalyzerConfigDocumentIds) - await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); - - // If all features are enabled for source generated documents, the solution crawler needs to - // include them in incremental analysis. - if (_solutionCrawlerOptions?.EnableDiagnosticsInSourceGeneratedFiles == true) - { - foreach (var document in await project.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false)) - await EnqueueDocumentWorkItemAsync(project, document.Id, document, invocationReasons).ConfigureAwait(false); - } - } - - private async Task EnqueueWorkItemAsync(IIncrementalAnalyzer analyzer, ReanalyzeScope scope, bool highPriority) - { - var solution = _registration.GetSolutionToAnalyze(); - var invocationReasons = highPriority ? InvocationReasons.ReanalyzeHighPriority : InvocationReasons.Reanalyze; - - foreach (var (project, documentId) in scope.GetDocumentIds(solution)) - await EnqueueWorkItemAsync(analyzer, project, documentId, document: null, invocationReasons).ConfigureAwait(false); - } - - private async Task EnqueueWorkItemAsync( - IIncrementalAnalyzer analyzer, Project project, DocumentId documentId, Document? document, InvocationReasons invocationReasons) - { - var priorityService = project.GetLanguageService(); - var isLowPriority = priorityService != null && await priorityService.IsLowPriorityAsync( - GetRequiredDocument(project, documentId, document), _shutdownToken).ConfigureAwait(false); - - _documentAndProjectWorkerProcessor.Enqueue( - new WorkItem(documentId, project.Language, invocationReasons, - isLowPriority, analyzer, _listener.BeginAsyncOperation("WorkItem"))); - } - - private async Task EnqueueWorkItemAsync(ProjectChanges projectChanges) - { - await EnqueueProjectConfigurationChangeWorkItemAsync(projectChanges).ConfigureAwait(false); - - foreach (var addedDocumentId in projectChanges.GetAddedDocuments()) - await EnqueueDocumentWorkItemAsync(projectChanges.NewProject, addedDocumentId, document: null, InvocationReasons.DocumentAdded).ConfigureAwait(false); - - foreach (var changedDocumentId in projectChanges.GetChangedDocuments()) - { - await EnqueueChangedDocumentWorkItemAsync(projectChanges.OldProject.GetRequiredDocument(changedDocumentId), projectChanges.NewProject.GetRequiredDocument(changedDocumentId)) - .ConfigureAwait(continueOnCapturedContext: false); - } - - foreach (var removedDocumentId in projectChanges.GetRemovedDocuments()) - await EnqueueDocumentWorkItemAsync(projectChanges.OldProject, removedDocumentId, document: null, InvocationReasons.DocumentRemoved).ConfigureAwait(false); - } - - private async Task EnqueueProjectConfigurationChangeWorkItemAsync(ProjectChanges projectChanges) - { - var oldProject = projectChanges.OldProject; - var newProject = projectChanges.NewProject; - - // TODO: why solution changes return Project not ProjectId but ProjectChanges return DocumentId not Document? - var projectConfigurationChange = InvocationReasons.Empty; - - if (!object.Equals(oldProject.ParseOptions, newProject.ParseOptions)) - { - projectConfigurationChange = projectConfigurationChange.With(InvocationReasons.ProjectParseOptionChanged); - } - - if (projectChanges.GetAddedMetadataReferences().Any() || - projectChanges.GetAddedProjectReferences().Any() || - projectChanges.GetAddedAnalyzerReferences().Any() || - projectChanges.GetRemovedMetadataReferences().Any() || - projectChanges.GetRemovedProjectReferences().Any() || - projectChanges.GetRemovedAnalyzerReferences().Any() || - !object.Equals(oldProject.CompilationOptions, newProject.CompilationOptions) || - !object.Equals(oldProject.AssemblyName, newProject.AssemblyName) || - !object.Equals(oldProject.Name, newProject.Name) || - !object.Equals(oldProject.AnalyzerOptions, newProject.AnalyzerOptions) || - !object.Equals(oldProject.DefaultNamespace, newProject.DefaultNamespace) || - !object.Equals(oldProject.OutputFilePath, newProject.OutputFilePath) || - !object.Equals(oldProject.OutputRefFilePath, newProject.OutputRefFilePath) || - !oldProject.CompilationOutputInfo.Equals(newProject.CompilationOutputInfo) || - oldProject.State.RunAnalyzers != newProject.State.RunAnalyzers) - { - projectConfigurationChange = projectConfigurationChange.With(InvocationReasons.ProjectConfigurationChanged); - } - - if (!projectConfigurationChange.IsEmpty) - { - await EnqueueFullProjectWorkItemAsync(projectChanges.NewProject, projectConfigurationChange).ConfigureAwait(false); - } - } - - private async Task EnqueueChangedDocumentWorkItemAsync(Document oldDocument, Document newDocument) - { - var differenceService = newDocument.GetLanguageService(); - - if (differenceService == null) - { - // For languages that don't use a Roslyn syntax tree, they don't export a document difference service. - // The whole document should be considered as changed in that case. - await EnqueueDocumentWorkItemAsync(newDocument.Project, newDocument.Id, newDocument, InvocationReasons.DocumentChanged).ConfigureAwait(false); - } - else - { - var differenceResult = await differenceService.GetDifferenceAsync(oldDocument, newDocument, _shutdownToken).ConfigureAwait(false); - - if (differenceResult != null) - await EnqueueDocumentWorkItemAsync(newDocument.Project, newDocument.Id, newDocument, differenceResult.ChangeType, differenceResult.ChangedMember).ConfigureAwait(false); - } - } - - internal TestAccessor GetTestAccessor() - { - return new TestAccessor(this); - } - - internal readonly struct TestAccessor - { - private readonly WorkCoordinator _workCoordinator; - - internal TestAccessor(WorkCoordinator workCoordinator) - { - _workCoordinator = workCoordinator; - } - - internal void WaitUntilCompletion(ImmutableArray workers) - { - var solution = _workCoordinator._registration.GetSolutionToAnalyze(); - var list = new List(); - - foreach (var project in solution.Projects) - { - foreach (var document in project.Documents) - { - list.Add(new WorkItem(document.Id, document.Project.Language, InvocationReasons.DocumentAdded, isLowPriority: false, activeMember: null, EmptyAsyncToken.Instance)); - } - } - - _workCoordinator._documentAndProjectWorkerProcessor.GetTestAccessor().WaitUntilCompletion(workers, list); - } - - internal void WaitUntilCompletion() - => _workCoordinator._documentAndProjectWorkerProcessor.GetTestAccessor().WaitUntilCompletion(); - } - } - - internal readonly struct ReanalyzeScope - { - private readonly SolutionId? _solutionId; - private readonly ISet? _projectOrDocumentIds; - - public ReanalyzeScope(SolutionId solutionId) - { - _solutionId = solutionId; - _projectOrDocumentIds = null; - } - - public ReanalyzeScope(IEnumerable? projectIds = null, IEnumerable? documentIds = null) - { - projectIds ??= SpecializedCollections.EmptyEnumerable(); - documentIds ??= SpecializedCollections.EmptyEnumerable(); - - _solutionId = null; - _projectOrDocumentIds = new HashSet(projectIds); - - foreach (var documentId in documentIds) - { - if (_projectOrDocumentIds.Contains(documentId.ProjectId)) - { - continue; - } - - _projectOrDocumentIds.Add(documentId); - } - } - - public bool HasMultipleDocuments => _solutionId != null || _projectOrDocumentIds?.Count > 1; - - public string GetLanguagesStringForTelemetry(Solution solution) - { - if (_solutionId != null && solution.Id != _solutionId) - { - // return empty if given solution is not - // same as solution this scope is created for - return string.Empty; - } - - using var pool = SharedPools.Default>().GetPooledObject(); - if (_solutionId != null) - { - pool.Object.UnionWith(solution.SolutionState.ProjectStates.Select(kv => kv.Value.Language)); - return string.Join(",", pool.Object); - } - - Contract.ThrowIfNull(_projectOrDocumentIds); - - foreach (var projectOrDocumentId in _projectOrDocumentIds) - { - switch (projectOrDocumentId) - { - case ProjectId projectId: - var project = solution.GetProject(projectId); - if (project != null) - { - pool.Object.Add(project.Language); - } - - break; - case DocumentId documentId: - var document = solution.GetDocument(documentId); - if (document != null) - { - pool.Object.Add(document.Project.Language); - } - - break; - default: - throw ExceptionUtilities.UnexpectedValue(projectOrDocumentId); - } - } - - return string.Join(",", pool.Object); - } - - public int GetDocumentCount(Solution solution) - { - if (_solutionId != null && solution.Id != _solutionId) - { - return 0; - } - - var count = 0; - if (_solutionId != null) - { - foreach (var projectState in solution.SolutionState.ProjectStates) - { - count += projectState.Value.DocumentStates.Count; - } - - return count; - } - - Contract.ThrowIfNull(_projectOrDocumentIds); - - foreach (var projectOrDocumentId in _projectOrDocumentIds) - { - switch (projectOrDocumentId) - { - case ProjectId projectId: - var project = solution.GetProject(projectId); - if (project != null) - { - count += project.DocumentIds.Count; - } - - break; - case DocumentId documentId: - count++; - break; - default: - throw ExceptionUtilities.UnexpectedValue(projectOrDocumentId); - } - } - - return count; - } - - public IEnumerable<(Project project, DocumentId documentId)> GetDocumentIds(Solution solution) - { - if (_solutionId != null && solution.Id != _solutionId) - { - yield break; - } - - if (_solutionId != null) - { - foreach (var project in solution.Projects) - { - foreach (var documentId in project.DocumentIds) - yield return (project, documentId); - } - - yield break; - } - - Contract.ThrowIfNull(_projectOrDocumentIds); - - foreach (var projectOrDocumentId in _projectOrDocumentIds) - { - switch (projectOrDocumentId) - { - case ProjectId projectId: - { - var project = solution.GetProject(projectId); - if (project != null) - { - foreach (var documentId in project.DocumentIds) - yield return (project, documentId); - } - - break; - } - case DocumentId documentId: - { - var project = solution.GetProject(documentId.ProjectId); - if (project != null) - { - // ReanalyzeScopes are created and held in a queue before they are processed later; it's possible the document - // that we queued for is no longer present. - if (project.ContainsDocument(documentId)) - yield return (project, documentId); - } - - break; - } - } - } - } - } -} -#endif From ae88cc3bd890cc33576a56282ec48ea87f666e50 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:06:56 -0800 Subject: [PATCH 092/151] simplify --- .../Features/Diagnostics/DiagnosticAnalyzerService.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 0c5f1249d66be..03fdcd0317742 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -77,13 +77,6 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) public void Reanalyze(Workspace workspace, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) { -#if false - var service = workspace.Services.GetService(); - if (service != null && _map.TryGetValue(workspace, out var analyzer)) - { - service.Reanalyze(workspace, analyzer, projectIds, documentIds, highPriority); - } -#endif } public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( From b79e41b137e3be594acd8b97934bf05a90bdc78c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:10:30 -0800 Subject: [PATCH 093/151] simplify --- .../DiagnosticAnalyzerService_IncrementalAnalyzer.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 4dc836504648a..7d1f9874d495f 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -22,17 +22,6 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) return _map.GetValue(workspace, _createIncrementalAnalyzer); } -#if false - public void ShutdownAnalyzerFrom(Workspace workspace) - { - // this should be only called once analyzer associated with the workspace is done. - if (_map.TryGetValue(workspace, out var analyzer)) - { - analyzer.Shutdown(); - } - } -#endif - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspace workspace) { From bf6a73e19a89a941d2965392bfbea952739e6057 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:11:08 -0800 Subject: [PATCH 094/151] simplify --- .../DiagnosticIncrementalAnalyzer.AnalysisData.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 49478b045cc30..0afc2829fd4ba 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -58,16 +58,6 @@ public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray< Debug.Assert(!oldItems.IsDefault); OldItems = oldItems; } - -#if false - public DocumentAnalysisData ToPersistData() - => new(Version, LineCount, Items); - - public bool FromCache - { - get { return OldItems.IsDefault; } - } -#endif } /// From 1088b1ede1ba241e1c4801b8a23316ea8ebbdacf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:11:52 -0800 Subject: [PATCH 095/151] simplify --- ...cIncrementalAnalyzer.CompilationManager.cs | 79 ------------------- 1 file changed, 79 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs index 8c4addbc97d78..da96714ada6e4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.CompilationManager.cs @@ -3,94 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { -#if false - /// - /// Return CompilationWithAnalyzer for given project with given stateSets - /// - private async Task GetOrCreateCompilationWithAnalyzersAsync(Project project, ImmutableArray stateSets, CancellationToken cancellationToken) - { - if (!project.SupportsCompilation) - { - return null; - } - - var ideOptions = AnalyzerService.GlobalOptions.GetIdeAnalyzerOptions(project); - - if (_projectCompilationsWithAnalyzers.TryGetValue(project, out var compilationWithAnalyzers)) - { - // We may have cached a null entry if we determiend that there are no actual analyzers to run. - if (compilationWithAnalyzers is null) - { - return null; - } - else if (((WorkspaceAnalyzerOptions)compilationWithAnalyzers.AnalysisOptions.Options!).IdeOptions == ideOptions) - { - // we have cached one, return that. - AssertAnalyzers(compilationWithAnalyzers, stateSets); - return compilationWithAnalyzers; - } - } - - // Create driver that holds onto compilation and associated analyzers - var newCompilationWithAnalyzers = await CreateCompilationWithAnalyzersAsync(project, ideOptions, stateSets, includeSuppressedDiagnostics: true, cancellationToken).ConfigureAwait(false); - - // Add new analyzer driver to the map - compilationWithAnalyzers = _projectCompilationsWithAnalyzers.GetValue(project, _ => newCompilationWithAnalyzers); - - // if somebody has beat us, make sure analyzers are good. - if (compilationWithAnalyzers != newCompilationWithAnalyzers) - { - AssertAnalyzers(compilationWithAnalyzers, stateSets); - } - - return compilationWithAnalyzers; - } -#endif - private static Task CreateCompilationWithAnalyzersAsync(Project project, IdeAnalyzerOptions ideOptions, IEnumerable stateSets, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) => DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, ideOptions, stateSets.Select(s => s.Analyzer), includeSuppressedDiagnostics, cancellationToken); - -#if false - private void ClearCompilationsWithAnalyzersCache(Project project) - => _projectCompilationsWithAnalyzers.Remove(project); - - private void ClearCompilationsWithAnalyzersCache() - { - // we basically eagarly clear the cache on some known changes - // to let CompilationWithAnalyzer go. - - // we create new conditional weak table every time netstandard as that's the only way it has to clear it. -#if NETSTANDARD - _projectCompilationsWithAnalyzers = new ConditionalWeakTable(); -#else - _projectCompilationsWithAnalyzers.Clear(); -#endif - } - - [Conditional("DEBUG")] - private static void AssertAnalyzers(CompilationWithAnalyzers? compilation, IEnumerable stateSets) - { - if (compilation == null) - { - // this can happen if project doesn't support compilation or no stateSets are given. - return; - } - - // make sure analyzers are same. - Contract.ThrowIfFalse(compilation.Analyzers.SetEquals(stateSets.Select(s => s.Analyzer).Where(a => !a.IsWorkspaceDiagnosticAnalyzer()))); - } -#endif } } From 9c55f85551fccf546d92cbb36b1b71520132e086 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:12:32 -0800 Subject: [PATCH 096/151] simplify --- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 3c350ea63e2ec..f50be70bbb662 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -65,23 +65,7 @@ public DiagnosticIncrementalAnalyzer( _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; _diagnosticAnalyzerRunner = new InProcOrRemoteHostAnalyzerRunner(analyzerInfoCache, analyzerService.Listener); - -#if false - GlobalOptions.AddOptionChangedHandler(this, OnGlobalOptionChanged); -#endif - } - -#if false - private void OnGlobalOptionChanged(object? sender, OptionChangedEventArgs e) - { - if (DiagnosticAnalyzerService.IsGlobalOptionAffectingDiagnostics(e.Option) && - GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - var service = Workspace.Services.GetService(); - service?.Reanalyze(Workspace, this, projectIds: null, documentIds: null, highPriority: false); - } } -#endif internal IGlobalOptionService GlobalOptions => AnalyzerService.GlobalOptions; internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; From 29a9085edd789bba6b51ff7a6f58c5fedab4b028 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:13:17 -0800 Subject: [PATCH 097/151] simplify --- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index f50be70bbb662..6a66f8884b68b 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -91,35 +91,6 @@ private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerRe ClearAllDiagnostics(stateSets, project.Id); } -#if false - public void Shutdown() - { - GlobalOptions.RemoveOptionChangedHandler(this, OnGlobalOptionChanged); - - var stateSets = _stateManager.GetAllStateSets(); - - AnalyzerService.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - var handleActiveFile = true; - using var _ = PooledHashSet.GetInstance(out var documentSet); - using var argsBuilder = TemporaryArray.Empty; - - foreach (var stateSet in stateSets) - { - var projectIds = stateSet.GetProjectsWithDiagnostics(); - foreach (var projectId in projectIds) - { - stateSet.CollectDocumentsWithDiagnostics(projectId, documentSet); - AddProjectDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), stateSet, projectId, documentSet, handleActiveFile); - documentSet.Clear(); - } - } - - raiseEvents(argsBuilder.ToImmutableAndClear()); - }); - } -#endif - private void ClearAllDiagnostics(ImmutableArray stateSets, ProjectId projectId) { AnalyzerService.RaiseBulkDiagnosticsUpdated(raiseEvents => @@ -222,14 +193,6 @@ private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary _telemetry.ReportAndClear(_correlationId); - - internal IEnumerable GetAnalyzersTestOnly(Project project) - => _stateManager.GetOrCreateStateSets(project).Select(s => s.Analyzer); -#endif - private static string GetDocumentLogMessage(string title, TextDocument document, DiagnosticAnalyzer analyzer) => $"{title}: ({document.Id}, {document.Project.Id}), ({analyzer})"; From e26a4fae22d29d87c3f1049c70704ef019584a37 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:13:57 -0800 Subject: [PATCH 098/151] simplify --- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 118 ------------------ 1 file changed, 118 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 9108fbd852b53..061bda0cde680 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -22,124 +22,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { -#if false - public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) - => AnalyzeDocumentForKindAsync(document, AnalysisKind.Syntax, cancellationToken); - public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) - => AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken); - public Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken) - => AnalyzeDocumentForKindAsync(textDocument, AnalysisKind.Syntax, cancellationToken); - - private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKind kind, CancellationToken cancellationToken) - { - try - { - if (!document.SupportsDiagnostics()) - { - return; - } - - var isActiveDocument = _documentTrackingService.TryGetActiveDocument() == document.Id; - var isOpenDocument = document.IsOpen(); - var isGeneratedRazorDocument = document.IsRazorDocument(); - - // Only analyze open/active documents, unless it is a generated Razor document. - if (!isActiveDocument && !isOpenDocument && !isGeneratedRazorDocument) - { - return; - } - - var stateSets = _stateManager.GetOrUpdateStateSets(document.Project); - var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync(document.Project, stateSets, cancellationToken).ConfigureAwait(false); - var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); - var backgroundAnalysisScope = GlobalOptions.GetBackgroundAnalysisScope(document.Project.Language); - var compilerDiagnosticsScope = GlobalOptions.GetBackgroundCompilerAnalysisScope(document.Project.Language); - - // TODO: Switch to a more reliable service to determine visible documents. - // DocumentTrackingService is known be unreliable at times. - var isVisibleDocument = _documentTrackingService.GetVisibleDocuments().Contains(document.Id); - - // We split the diagnostic computation for document into following steps: - // 1. Try to get cached diagnostics for each analyzer, while computing the set of analyzers that do not have cached diagnostics. - // 2. Execute all the non-cached analyzers with a single invocation into CompilationWithAnalyzers. - // 3. Fetch computed diagnostics per-analyzer from the above invocation, and cache and raise diagnostic reported events. - // In near future, the diagnostic computation invocation into CompilationWithAnalyzers will be moved to OOP. - // This should help simplify and/or remove the IDE layer diagnostic caching in devenv process. - - // First attempt to fetch diagnostics from the cache, while computing the analyzers that are not cached. - using var _ = ArrayBuilder<(DiagnosticAnalyzer analyzer, ActiveFileState state)>.GetInstance(out var nonCachedAnalyzersAndStates); - using var argsBuilder = TemporaryArray.Empty; - foreach (var stateSet in stateSets) - { - var (activeFileState, existingData) = TryGetCachedDocumentAnalysisData(document, stateSet, kind, version, - backgroundAnalysisScope, compilerDiagnosticsScope, isActiveDocument, isVisibleDocument, - isOpenDocument, isGeneratedRazorDocument, cancellationToken, out var isAnalyzerSuppressed); - if (existingData.HasValue) - { - PersistAndAddDiagnosticsArgsIfNeeded(ref argsBuilder.AsRef(), existingData.Value, stateSet.Analyzer, activeFileState); - } - else if (!isAnalyzerSuppressed) - { - nonCachedAnalyzersAndStates.Add((stateSet.Analyzer, activeFileState)); - } - } - - // Send events for cached analyzers as a batch. The preceding loop is expected to quickly aggregate - // results from cached analyzers, since it will not wait for analyzers that are not already complete. - AnalyzerService.RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - - // Then, compute the diagnostics for non-cached state sets, and cache and raise diagnostic reported events for these diagnostics. - if (nonCachedAnalyzersAndStates.Count > 0) - { - var analysisScope = new DocumentAnalysisScope(document, span: null, nonCachedAnalyzersAndStates.SelectAsArray(s => s.analyzer), kind); - var executor = new DocumentAnalysisExecutor(analysisScope, compilationWithAnalyzers, _diagnosticAnalyzerRunner, isExplicit: false, logPerformanceInfo: false, onAnalysisException: OnAnalysisException); - var logTelemetry = GlobalOptions.GetOption(DiagnosticOptionsStorage.LogTelemetryForBackgroundAnalyzerExecution); - foreach (var (analyzer, state) in nonCachedAnalyzersAndStates) - { - var computedData = await ComputeDocumentAnalysisDataAsync(executor, analyzer, state, logTelemetry, cancellationToken).ConfigureAwait(false); - - PersistAndAddDiagnosticsArgsIfNeeded(ref argsBuilder.AsRef(), computedData, analyzer, state); - - // Send events for non-cached analyzers as soon as they complete, to avoid delaying error list - // updates when a subset of the analyzers takes a noticeably longer time to complete. - AnalyzerService.RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - } - } - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - - void PersistAndAddDiagnosticsArgsIfNeeded( - ref TemporaryArray builder, - DocumentAnalysisData result, DiagnosticAnalyzer analyzer, ActiveFileState state) - { - if (result.FromCache == true) - { - AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, result.Items); - return; - } - - // no cancellation after this point. - state.Save(kind, result.ToPersistData()); - - AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, result.OldItems, result.Items); - } - - void OnAnalysisException() - { - // Do not re-use cached CompilationWithAnalyzers instance in presence of an exception, as the underlying analysis state might be corrupt. - ClearCompilationsWithAnalyzersCache(document.Project); - } - } - - public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) - { - await AnalyzeProjectAsync(project, forceAnalyzerRun: false, cancellationToken).ConfigureAwait(false); - } -#endif - public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) => AnalyzeProjectAsync(project, forceAnalyzerRun: true, cancellationToken); From 69580e2a1fad76cc3761564cb3a3ae4d93c89a67 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:15:11 -0800 Subject: [PATCH 099/151] simplify --- .../Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 6a66f8884b68b..3b430981ab6b3 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -3,19 +3,16 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; From ccc064cb9ae8025a9bb6b06e87218662a2b2090d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:15:26 -0800 Subject: [PATCH 100/151] simplify --- ...scSolutionCrawlerWorkspaceEventListener.cs | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs diff --git a/src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs b/src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs deleted file mode 100644 index 65464927eb34b..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/SolutionCrawler/MiscSolutionCrawlerWorkspaceEventListener.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.SolutionCrawler -{ - [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.MiscellaneousFiles), Shared] - internal sealed class MiscSolutionCrawlerWorkspaceEventListener : IEventListener, IEventListenerStoppable - { - private readonly IGlobalOptionService _globalOptions; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public MiscSolutionCrawlerWorkspaceEventListener(IGlobalOptionService globalOptions) - { - _globalOptions = globalOptions; - } - - public void StartListening(Workspace workspace, object serviceOpt) - { - if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - DiagnosticProvider.Enable(workspace); - } - - public void StopListening(Workspace workspace) - => DiagnosticProvider.Disable(workspace); - } -} -#endif From b180a571c4de238442be31836a1b72fee8b2bbb8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:15:43 -0800 Subject: [PATCH 101/151] simplify --- src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs index 955ea13d8aa0a..eea746fa40cab 100644 --- a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs +++ b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs @@ -49,11 +49,6 @@ public async Task RunAsync(CancellationToken cancellationToken) var workspaceConfigurationService = (AnalyzerRunnerWorkspaceConfigurationService)_workspace.Services.GetRequiredService(); workspaceConfigurationService.Options = new(CacheStorage: usePersistentStorage ? StorageDatabase.SQLite : StorageDatabase.None); -#if false - var solutionCrawlerRegistrationService = (SolutionCrawlerRegistrationService)_workspace.Services.GetRequiredService(); - solutionCrawlerRegistrationService.Register(_workspace); -#endif - if (usePersistentStorage) { var persistentStorageService = _workspace.Services.SolutionServices.GetPersistentStorageService(); @@ -72,9 +67,6 @@ public async Task RunAsync(CancellationToken cancellationToken) incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).SingleOrDefault(provider => provider.Metadata.WorkspaceKinds.Contains(WorkspaceKind.RemoteWorkspace))?.Value; incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).Single(provider => provider.Metadata.WorkspaceKinds is []).Value; var incrementalAnalyzer = incrementalAnalyzerProvider.CreateIncrementalAnalyzer(_workspace); -#if false - solutionCrawlerRegistrationService.GetTestAccessor().WaitUntilCompletion(_workspace, ImmutableArray.Create(incrementalAnalyzer)); -#endif } } } From 7ecbd9aa149ffc186bb71206f9117bdbed46220b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:16:06 -0800 Subject: [PATCH 102/151] simplify --- .../Diagnostics/DiagnosticProgressReporter.cs | 204 ------------------ 1 file changed, 204 deletions(-) delete mode 100644 src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs diff --git a/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs b/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs deleted file mode 100644 index 9d5e3130d4df3..0000000000000 --- a/src/VisualStudio/Core/Def/Diagnostics/DiagnosticProgressReporter.cs +++ /dev/null @@ -1,204 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using System; -using System.ComponentModel.Composition; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.VisualStudio.TaskStatusCenter; -using Roslyn.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics -{ - [Export(typeof(TaskCenterSolutionAnalysisProgressReporter))] - internal sealed class TaskCenterSolutionAnalysisProgressReporter - { - private static readonly TimeSpan s_minimumInterval = TimeSpan.FromMilliseconds(200); - private static readonly TaskHandlerOptions _options = new() - { - Title = ServicesVSResources.Running_low_priority_background_processes, - ActionsAfterCompletion = CompletionActions.None - }; - - private IVsTaskStatusCenterService? _taskCenterService; - - #region Fields protected by _lock - - /// - /// Gate access to reporting sln crawler events so we cannot - /// report UI changes concurrently. - /// - private readonly object _lock = new(); - private readonly VisualStudioWorkspace _workspace; - private readonly IVsService _taskStatusCenterService; - - /// - /// Task used to trigger throttled UI updates in an interval - /// defined by - /// Protected from concurrent access by the - /// - private Task? _intervalTask; - - /// - /// Stores the last shown - /// Protected from concurrent access by the - /// - private ProgressData _lastProgressData; - - /// - /// Task used to ensure serialization of UI updates. - /// Protected from concurrent access by the - /// - private Task _updateUITask = Task.CompletedTask; - - #endregion - - #region Fields protected by _updateUITask running serially - - /// - /// Task handler to provide a task to the - /// Protected from concurrent access due to serialization from - /// - private ITaskHandler? _taskHandler; - - /// - /// Stores the currently running task center task. - /// This is manually started and completed based on receiving start / stop events - /// from the - /// Protected from concurrent access due to serialization from - /// - private TaskCompletionSource? _taskCenterTask; - - /// - /// Unfortunately, is only reported - /// when the is - /// So we have to store the count separately for the UI so that we do not overwrite the last reported count with 0. - /// Protected from concurrent access due to serialization from - /// - private int _lastPendingItemCount; - - #endregion - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public TaskCenterSolutionAnalysisProgressReporter( - VisualStudioWorkspace workspace, - IVsService taskStatusCenterService) - { - _workspace = workspace; - _taskStatusCenterService = taskStatusCenterService; - } - - public async Task InitializeAsync() - { - _taskCenterService = await _taskStatusCenterService.GetValueAsync().ConfigureAwait(false); - -#if false - var crawlerService = _workspace.Services.GetRequiredService(); - var reporter = crawlerService.GetProgressReporter(_workspace); - - if (reporter.InProgress) - { - // The reporter was already sending events before we were able to subscribe, so trigger an update to the task center. - OnSolutionCrawlerProgressChanged(this, new ProgressData(ProgressStatus.Started, pendingItemCount: null)); - } - - reporter.ProgressChanged += OnSolutionCrawlerProgressChanged; -#endif - } - - /// - /// Retrieve and throttle solution crawler events to be sent to the progress reporter UI. - /// - /// there is no concurrent call to this method since ISolutionCrawlerProgressReporter will serialize all - /// events to preserve event ordering - /// - /// - public void OnSolutionCrawlerProgressChanged(object sender, ProgressData progressData) - { - lock (_lock) - { - _lastProgressData = progressData; - - // The task is running which will update the progress. - if (_intervalTask != null) - { - return; - } - - // Kick off task to update the UI after a delay to pick up any new events. - _intervalTask = Task.Delay(s_minimumInterval).ContinueWith(_ => ReportProgress(), - CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - } - - private void ReportProgress() - { - lock (_lock) - { - var data = _lastProgressData; - _intervalTask = null; - - _updateUITask = _updateUITask.ContinueWith(_ => UpdateUI(data), - CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); - } - } - - private void UpdateUI(ProgressData progressData) - { - if (progressData.Status == ProgressStatus.Stopped) - { - StopTaskCenter(); - return; - } - - // Update the pending item count if the progress data specifies a value. - if (progressData.PendingItemCount.HasValue) - { - _lastPendingItemCount = progressData.PendingItemCount.Value; - } - - // Start the task center task if not already running. - if (_taskHandler == null) - { - Contract.ThrowIfNull(_taskCenterService); - - // Register a new task handler to handle a new task center task. - // Each task handler can only register one task, so we must create a new one each time we start. - _taskHandler = _taskCenterService.PreRegister(_options, data: default); - - // Create a new non-completed task to be tracked by the task handler. - _taskCenterTask = new TaskCompletionSource(); - _taskHandler.RegisterTask(_taskCenterTask.Task); - } - - var statusMessage = progressData.Status == ProgressStatus.Paused - ? ServicesVSResources.Paused_0_tasks_in_queue - : ServicesVSResources.Evaluating_0_tasks_in_queue; - - _taskHandler.Progress.Report(new TaskProgressData - { - ProgressText = string.Format(statusMessage, _lastPendingItemCount), - CanBeCanceled = false, - PercentComplete = null, - }); - } - - private void StopTaskCenter() - { - // Mark the progress task as completed so it shows complete in the task center. - _taskCenterTask?.TrySetResult(default); - - // Clear tasks and data. - _taskCenterTask = null; - _taskHandler = null; - _lastProgressData = default; - _lastPendingItemCount = 0; - } - } -} -#endif From d16f3fa22bd6fe21d63c293253ae2a011b942bf9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:16:35 -0800 Subject: [PATCH 103/151] simplify --- .../Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs | 5 ----- src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs | 5 ----- 2 files changed, 10 deletions(-) diff --git a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs index fb894ee47bf18..6aec52668082b 100644 --- a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs +++ b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs @@ -123,11 +123,6 @@ private VisualStudioRemoteHostClientProvider( (_globalOptions.GetOption(RemoteHostOptionsStorage.OOPCoreClr) ? RemoteProcessConfiguration.Core : 0) | (_globalOptions.GetOption(RemoteHostOptionsStorage.OOPServerGCFeatureFlag) ? RemoteProcessConfiguration.ServerGC : 0); -#if false - | - (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler) ? RemoteProcessConfiguration.EnableSolutionCrawler : 0); -#endif - // VS AsyncLazy does not currently support cancellation: var client = await ServiceHubRemoteHostClient.CreateAsync(Services, configuration, _listenerProvider, serviceBroker, _callbackDispatchers, CancellationToken.None).ConfigureAwait(false); diff --git a/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs b/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs index 8896025547878..9907fee627cde 100644 --- a/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs +++ b/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs @@ -18,10 +18,5 @@ internal enum RemoteProcessConfiguration /// Remote host uses server GC. /// ServerGC = 1 << 1, - - /// - /// Enable solution crawler in remote workspace. - /// - EnableSolutionCrawler = 1 << 2 } } From afa4b65ef282796d8b2210c29a64f6a44e35a25f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:16:48 -0800 Subject: [PATCH 104/151] simplify --- src/VisualStudio/Core/Def/RoslynPackage.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 71a105241e509..0792b90a397e3 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -223,9 +223,6 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation // we need to load it as early as possible since we can have errors from // package from each language very early -#if false - await this.ComponentModel.GetService().InitializeAsync().ConfigureAwait(false); -#endif await this.ComponentModel.GetService().InitializeAsync(this).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); await this.ComponentModel.GetService().InitializeAsync(this, cancellationToken).ConfigureAwait(false); From e8c5229e3c48afadb3e45d39733dda571eb949d2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:23:28 -0800 Subject: [PATCH 105/151] simplify --- .../IRemoteDiagnosticAnalyzerService.cs | 1 - .../Services/ServiceHubServicesTests.cs | 10 ---------- .../Remote/Core/ServiceHubRemoteHostClient.cs | 7 ------- .../RemoteDiagnosticAnalyzerService.cs | 17 ----------------- 4 files changed, 35 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs index b7846566646a3..f82dfabcc61cb 100644 --- a/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs @@ -20,7 +20,6 @@ internal interface IRemoteDiagnosticAnalyzerService ValueTask CalculateDiagnosticsAsync(Checksum solutionChecksum, DiagnosticArguments arguments, CancellationToken cancellationToken); ValueTask> GetSourceGeneratorDiagnosticsAsync(Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); ValueTask ReportAnalyzerPerformanceAsync(ImmutableArray snapshot, int unitCount, bool forSpanAnalysis, CancellationToken cancellationToken); - ValueTask StartSolutionCrawlerAsync(CancellationToken cancellationToken); } [DataContract] diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 34f888fea9dbe..1406a61889e5c 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -131,11 +131,6 @@ public async Task TestDesignerAttributes() using var client = await InProcRemoteHostClient.GetTestClientAsync(workspace).ConfigureAwait(false); var remoteWorkspace = client.GetRemoteWorkspace(); - // Start solution crawler in the remote workspace: - await client.TryInvokeAsync( - (service, cancellationToken) => service.StartSolutionCrawlerAsync(cancellationToken), - CancellationToken.None).ConfigureAwait(false); - var cancellationTokenSource = new CancellationTokenSource(); var solution = workspace.CurrentSolution; @@ -187,11 +182,6 @@ public async Task TestDesignerAttributesUnsupportedLanguage() using var client = await InProcRemoteHostClient.GetTestClientAsync(workspace).ConfigureAwait(false); var remoteWorkspace = client.GetRemoteWorkspace(); - // Start solution crawler in the remote workspace: - await client.TryInvokeAsync( - (service, cancellationToken) => service.StartSolutionCrawlerAsync(cancellationToken), - CancellationToken.None).ConfigureAwait(false); - var cancellationTokenSource = new CancellationTokenSource(); var solution = workspace.CurrentSolution; diff --git a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs index 648094d001a2f..003a7f9ac5e8d 100644 --- a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs +++ b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs @@ -101,13 +101,6 @@ public static async Task CreateAsync( hubClient.Logger.TraceEvent(TraceEventType.Error, 1, "Roslyn ServiceHub process initialization failed."); } - if (configuration.HasFlag(RemoteProcessConfiguration.EnableSolutionCrawler)) - { - await client.TryInvokeAsync( - (service, cancellationToken) => service.StartSolutionCrawlerAsync(cancellationToken), - cancellationToken).ConfigureAwait(false); - } - await client.TryInvokeAsync( (service, cancellationToken) => service.EnableAsync(AsynchronousOperationListenerProvider.IsEnabled, listenerProvider.DiagnosticTokensEnabled, cancellationToken), cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs index 32f151b65da1c..751f60f21e592 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs @@ -34,23 +34,6 @@ public RemoteDiagnosticAnalyzerService(in ServiceConstructionArguments arguments { } - /// - /// Remote API. - /// - public ValueTask StartSolutionCrawlerAsync(CancellationToken cancellationToken) - { - return RunServiceAsync(cancellationToken => - { - // register solution crawler: - var workspace = GetWorkspace(); -#if false - workspace.Services.GetRequiredService().Register(workspace); -#endif - - return ValueTaskFactory.CompletedTask; - }, cancellationToken); - } - /// /// Calculate diagnostics. this works differently than other ones such as todo comments or designer attribute scanner /// since in proc and out of proc runs quite differently due to concurrency and due to possible amount of data From 9215881764162e6a5d9216e0c234903808210ceb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:26:29 -0800 Subject: [PATCH 106/151] simplify --- .../Services/SolutionServiceTests.cs | 78 ------------------- 1 file changed, 78 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index edbefa8df25a1..3f6703acfb73d 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -329,54 +329,6 @@ await VerifySolutionUpdate(workspace, s => }); } -#if false - [Fact] - public async Task TestRemoteWorkspaceSolutionCrawler() - { - var code = @"class Test { void Method() { } }"; - - // create base solution - using var workspace = TestWorkspace.CreateCSharp(code); - using var remoteWorkspace = CreateRemoteWorkspace(); - - // Start solution crawler in the remote workspace: - remoteWorkspace.Services.GetRequiredService().Register(remoteWorkspace); - - // create solution service - var solution = workspace.CurrentSolution; - var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); - - // update primary workspace - var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, solution.WorkspaceVersion, CancellationToken.None); - - // get solution in remote host - var remoteSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); - - // get solution cralwer in remote host - var solutionCrawlerService = remoteSolution.Services.GetService() as SolutionCrawlerRegistrationService; - Assert.NotNull(solutionCrawlerService); - - // check remote workspace has enabled solution crawler in remote host - var testAnalyzerProvider = new TestAnalyzerProvider(); - solutionCrawlerService.AddAnalyzerProvider( - testAnalyzerProvider, - new IncrementalAnalyzerProviderMetadata("Test", highPriorityForActiveFile: false, [WorkspaceKind.RemoteWorkspace])); - - // check our solution crawler has ran - Assert.True(await testAnalyzerProvider.Analyzer.Called); - - testAnalyzerProvider.Analyzer.Reset(); - - // update remote workspace - remoteSolution = remoteSolution.WithDocumentText(remoteSolution.Projects.First().Documents.First().Id, SourceText.From(code + " class Test2 { }")); - await remoteWorkspace.GetTestAccessor().TryUpdateWorkspaceCurrentSolutionAsync(remoteSolution, solution.WorkspaceVersion + 1); - - // check solution update correctly ran solution crawler - Assert.True(await testAnalyzerProvider.Analyzer.Called); - } -#endif - [Fact] public async Task TestRemoteWorkspace() { @@ -973,35 +925,5 @@ private static async Task GetAssetProviderAsync(Workspace workspa return new AssetProvider(sessionId, storage, assetSource, remoteWorkspace.Services.GetService()); } - -#if false - private class TestAnalyzerProvider : IIncrementalAnalyzerProvider - { - public readonly TestAnalyzer Analyzer = new TestAnalyzer(); - - public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) - { - return Analyzer; - } - - public class TestAnalyzer : IncrementalAnalyzerBase - { - private TaskCompletionSource _source = new TaskCompletionSource(); - - public override Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) - { - _source.SetResult(true); - return Task.CompletedTask; - } - - public Task Called => _source.Task; - - public void Reset() - { - _source = new TaskCompletionSource(); - } - } - } -#endif } } From 86c84559723bf09c4cf8b3e96094459c4aaa7bb7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:27:29 -0800 Subject: [PATCH 107/151] simplify --- .../Client/RemoteLanguageServiceWorkspace.cs | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index 103bf5b816cf9..912456d3fa26e 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -48,7 +48,6 @@ internal sealed class RemoteLanguageServiceWorkspace : CodeAnalysis.Workspace, I private readonly IServiceProvider _serviceProvider; private readonly IThreadingContext _threadingContext; - private readonly IGlobalOptionService _globalOptions; private readonly OpenTextBufferProvider _openTextBufferProvider; private readonly IVsFolderWorkspaceService _vsFolderWorkspaceService; @@ -78,7 +77,6 @@ internal sealed class RemoteLanguageServiceWorkspace : CodeAnalysis.Workspace, I [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public RemoteLanguageServiceWorkspace( ExportProvider exportProvider, - IGlobalOptionService globalOptions, OpenTextBufferProvider openTextBufferProvider, IVsFolderWorkspaceService vsFolderWorkspaceService, SVsServiceProvider serviceProvider, @@ -92,7 +90,6 @@ public RemoteLanguageServiceWorkspace( _openTextBufferProvider = openTextBufferProvider; _openTextBufferProvider.AddListener(this); _threadingContext = threadingContext; - _globalOptions = globalOptions; _vsFolderWorkspaceService = vsFolderWorkspaceService; _remoteWorkspaceRootPaths = ImmutableHashSet.Empty; @@ -119,8 +116,6 @@ public async Task SetSessionAsync(CollaborationSession session) { _session = session; - StartSolutionCrawler(); - // Get the initial workspace roots and update any files that have been opened. await UpdatePathsToRemoteFilesAsync(session).ConfigureAwait(false); @@ -195,7 +190,6 @@ public void EndSession() { _session = null; _vsFolderWorkspaceService.OnActiveWorkspaceChanged -= OnActiveWorkspaceChangedAsync; - StopSolutionCrawler(); // Clear the remote paths on end of session. Live share handles closing all the files. using (s_RemotePathsGate.DisposableWait()) @@ -524,20 +518,5 @@ private static void UpdateText(ITextBuffer textBuffer, SourceText text) edit.Apply(); } } - - private void StartSolutionCrawler() - { -#if false - if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - DiagnosticProvider.Enable(this); -#endif - } - - private void StopSolutionCrawler() - { -#if false - DiagnosticProvider.Disable(this); -#endif - } } } From 7ab7f73d6d607426828c3868dfdf086b6525b0c4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:27:51 -0800 Subject: [PATCH 108/151] simplify --- .../Diagnostics/DiagnosticProvider.cs | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs deleted file mode 100644 index 78264d3860ce6..0000000000000 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticProvider.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -#nullable disable - -using System; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.SolutionCrawler; - -namespace Microsoft.CodeAnalysis; - -/// -/// Provide a way for users to turn on and off analyzing workspace for compiler diagnostics -/// -internal static class DiagnosticProvider -{ - public static void Enable(Workspace workspace) - { - var service = workspace.Services.GetService(); - service.Register(workspace); - } - - public static void Disable(Workspace workspace) - { - var service = workspace.Services.GetService(); - service.Unregister(workspace); - } -} -#endif From 8e98c2a14265cc3c58f26306c8d80508d5270070 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:28:20 -0800 Subject: [PATCH 109/151] simplify --- .../SolutionCrawler/IIncrementalAnalyzer.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs index ffd4fce6f68d9..b3a7868333f5c 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs @@ -12,23 +12,4 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler; internal interface IIncrementalAnalyzer { -#if false - Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken); - Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken); - Task DocumentCloseAsync(Document document, CancellationToken cancellationToken); - Task DocumentOpenAsync(Document document, CancellationToken cancellationToken); - Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken); - Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken); - Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken); - - Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken); - Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken); - - Task NonSourceDocumentOpenAsync(TextDocument textDocument, CancellationToken cancellationToken); - Task NonSourceDocumentCloseAsync(TextDocument textDocument, CancellationToken cancellationToken); - - Task AnalyzeNonSourceDocumentAsync(TextDocument textDocument, InvocationReasons reasons, CancellationToken cancellationToken); - void LogAnalyzerCountSummary(); - void Shutdown(); -#endif } From 1d735b31dd721e69fa8122f4d3754090c693c0a6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:28:30 -0800 Subject: [PATCH 110/151] simplify --- .../ISolutionCrawlerRegistrationService.cs | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs b/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs deleted file mode 100644 index e37ead62d16fa..0000000000000 --- a/src/Workspaces/Core/Portable/SolutionCrawler/ISolutionCrawlerRegistrationService.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -using Microsoft.CodeAnalysis.Host; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -/// -/// Register a solution crawler for a particular workspace -/// -internal interface ISolutionCrawlerRegistrationService : IWorkspaceService -{ - void Register(Workspace workspace); - - /// - /// Unregisters solution crawler for given . - /// No-op if never registered or already unregistered. - /// - void Unregister(Workspace workspace, bool blockingShutdown = false); - - void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata); -} -#endif From 2786a90fc860172d607ad90ea4a6528e22d4fcc1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:28:41 -0800 Subject: [PATCH 111/151] simplify --- ...NullSolutionCrawlerRegisterationService.cs | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs b/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs deleted file mode 100644 index 3d60271d61f9c..0000000000000 --- a/src/Workspaces/Core/Portable/SolutionCrawler/NullSolutionCrawlerRegisterationService.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if false -#nullable disable - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -/// -/// null implementation of the service. it doesn't do anything since there is no way to observe -/// its impact in this layer. -/// -[ExportWorkspaceService(typeof(ISolutionCrawlerRegistrationService), ServiceLayer.Default), Shared] -internal partial class NullSolutionCrawlerRegistrationService : ISolutionCrawlerRegistrationService -{ - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public NullSolutionCrawlerRegistrationService() - { - } - - public void Register(Workspace workspace) - { - // base implementation do nothing. - } - - public void Unregister(Workspace workspace, bool blockingShutdown = false) - { - // base implementation do nothing. - } - - public void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata) - { - // base implementation do nothing. - } -} -#endif From 28ff753f571afc53836734319792491c1768c625 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:28:56 -0800 Subject: [PATCH 112/151] simplify --- .../Host/RemoteDocumentDifferenceService.cs | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs index 0a19d7e3d626f..98767800238b5 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs @@ -46,35 +46,6 @@ public VisualBasicDocumentDifferenceService() public Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { return SpecializedTasks.Null(); -#if false - // in remote workspace, we don't trust any version based on VersionStamp. we only trust content based information such as - // checksum or tree comparison and etc. - - // first check checksum - var oldTextChecksum = (await oldDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false)).Text; - var newTextChecksum = (await newDocument.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false)).Text; - if (oldTextChecksum == newTextChecksum) - { - // null means nothing has changed. - return null; - } - - var oldRoot = await oldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - - // the service is only registered for C# and VB documents, which must have syntax trees: - Contract.ThrowIfNull(oldRoot); - Contract.ThrowIfNull(newRoot); - - if (oldRoot.IsEquivalentTo(newRoot, topLevel: true)) - { - // only method body changed - return new DocumentDifferenceResult(InvocationReasons.SyntaxChanged); - } - - // semantic has changed as well. - return new DocumentDifferenceResult(InvocationReasons.DocumentChanged); -#endif } } } From 48f07c503283c2109eda217968bdd5b48f8b019a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:29:19 -0800 Subject: [PATCH 113/151] simplify --- src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs index eded02c0cf074..e571f571bc2e9 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs @@ -39,14 +39,6 @@ internal RemoteWorkspace(HostServices hostServices) { } - protected override void Dispose(bool finalize) - { - base.Dispose(finalize); -#if false - Services.GetRequiredService().Unregister(this); -#endif - } - public AssetProvider CreateAssetProvider(Checksum solutionChecksum, SolutionAssetCache assetCache, IAssetSource assetSource) { var serializerService = Services.GetRequiredService(); From 99fbc9c1b3caaf5bccccd7bcc538d8aa9d144da3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:29:44 -0800 Subject: [PATCH 114/151] simplify --- src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs index e571f571bc2e9..b68de4597e65d 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs @@ -3,14 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Serialization; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using static Microsoft.VisualStudio.Threading.ThreadingTools; From e4a89779bfc9c32e8bcc283e4f802d5256637516 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:33:30 -0800 Subject: [PATCH 115/151] simplify --- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 205 ------------------ 1 file changed, 205 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 061bda0cde680..451b730994044 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -74,197 +74,6 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C } } -#if false - public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) - => TextDocumentOpenAsync(document, cancellationToken); - - public Task NonSourceDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) - => TextDocumentOpenAsync(document, cancellationToken); - - private async Task TextDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.Diagnostics_DocumentOpen, GetOpenLogMessage, document, cancellationToken)) - { - var stateSets = _stateManager.GetStateSets(document.Project); - - // let other component knows about this event - ClearCompilationsWithAnalyzersCache(); - - // can not be canceled - foreach (var stateSet in stateSets) - await stateSet.OnDocumentOpenedAsync(document).ConfigureAwait(false); - } - } - - public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) - => TextDocumentCloseAsync(document, cancellationToken); - - public Task NonSourceDocumentCloseAsync(TextDocument document, CancellationToken cancellationToken) - => TextDocumentCloseAsync(document, cancellationToken); - - private async Task TextDocumentCloseAsync(TextDocument document, CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.Diagnostics_DocumentClose, GetResetLogMessage, document, cancellationToken)) - { - var stateSets = _stateManager.GetStateSets(document.Project); - - // let other components knows about this event - ClearCompilationsWithAnalyzersCache(); - - // can not be canceled - var documentHadDiagnostics = false; - foreach (var stateSet in stateSets) - documentHadDiagnostics |= await stateSet.OnDocumentClosedAsync(document, GlobalOptions).ConfigureAwait(false); - - RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); - } - } - - private Task TextDocumentResetAsync(TextDocument document, CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.Diagnostics_DocumentReset, GetResetLogMessage, document, cancellationToken)) - { - var stateSets = _stateManager.GetStateSets(document.Project); - - // let other components knows about this event - ClearCompilationsWithAnalyzersCache(); - // can not be canceled - var documentHadDiagnostics = false; - foreach (var stateSet in stateSets) - documentHadDiagnostics |= stateSet.OnDocumentReset(document); - - RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(document, stateSets, documentHadDiagnostics); - } - - return Task.CompletedTask; - } - - private void RaiseDiagnosticsRemovedIfRequiredForClosedOrResetDocument(TextDocument document, IEnumerable stateSets, bool documentHadDiagnostics) - { - // If there was no diagnostic reported for this document, nothing to clean up - // This is done for Perf to reduce raising events unnecessarily. - if (!documentHadDiagnostics) - return; - - // If full solution analysis is enabled for both compiler diagnostics and analyzers, - // we don't need to clear diagnostics for individual documents on document close/reset. - // This is done for Perf to reduce raising events unnecessarily. - var _ = GlobalOptions.IsFullSolutionAnalysisEnabled(document.Project.Language, out var compilerFullAnalysisEnabled, out var analyzersFullAnalysisEnabled); - if (compilerFullAnalysisEnabled && analyzersFullAnalysisEnabled) - return; - - var removeDiagnosticsOnDocumentClose = GlobalOptions.GetOption(SolutionCrawlerOptionsStorage.RemoveDocumentDiagnosticsOnDocumentClose, document.Project.Language); - - if (!removeDiagnosticsOnDocumentClose) - { - return; - } - - RaiseDiagnosticsRemovedForDocument(document.Id, stateSets); - } - - public async Task ActiveDocumentSwitchedAsync(TextDocument document, CancellationToken cancellationToken) - { - // Retrigger analysis of newly active document to always get up-to-date diagnostics. - // Note that we do so regardless of the current background analysis scope, - // as we might have switched the document _while_ the diagnostic refresh was in progress for - // all open documents, which can lead to cancellation of diagnostic recomputation task - // for the newly active document. This can lead to a race condition where we end up with - // stale diagnostics for the active document. We avoid that by always recomputing - // the diagnostics for the newly active document whenever active document is switched. - - // First reset the document states. - await TextDocumentResetAsync(document, cancellationToken).ConfigureAwait(false); - - // Trigger syntax analysis. - await AnalyzeDocumentForKindAsync(document, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false); - - // Trigger semantic analysis for source documents. Non-source documents do not support semantic analysis. - if (document is Document) - await AnalyzeDocumentForKindAsync(document, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false); - } - - public Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.Diagnostics_RemoveDocument, GetRemoveLogMessage, documentId, CancellationToken.None)) - { - var stateSets = _stateManager.GetStateSets(documentId.ProjectId); - - // let other components knows about this event - ClearCompilationsWithAnalyzersCache(); - - var changed = false; - foreach (var stateSet in stateSets) - changed |= stateSet.OnDocumentRemoved(documentId); - - // if there was no diagnostic reported for this document, nothing to clean up - // this is Perf to reduce raising events unnecessarily. - if (changed) - RaiseDiagnosticsRemovedForDocument(documentId, stateSets); - } - - return Task.CompletedTask; - } - - private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerable stateSets) - { - // remove all diagnostics for the document - AnalyzerService.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - using var argsBuilder = TemporaryArray.Empty; - foreach (var stateSet in stateSets) - { - // clear all doucment diagnostics - AddDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), documentId, solution: null, stateSet.Analyzer, AnalysisKind.Syntax); - AddDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), documentId, solution: null, stateSet.Analyzer, AnalysisKind.Semantic); - AddDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), documentId, solution: null, stateSet.Analyzer, AnalysisKind.NonLocal); - } - - raiseEvents(argsBuilder.ToImmutableAndClear()); - }); - } - - public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellation) - { - using (Logger.LogBlock(FunctionId.Diagnostics_RemoveProject, GetRemoveLogMessage, projectId, CancellationToken.None)) - { - var stateSets = _stateManager.GetStateSets(projectId); - - // let other components knows about this event - ClearCompilationsWithAnalyzersCache(); - var changed = _stateManager.OnProjectRemoved(stateSets, projectId); - - // if there was no diagnostic reported for this project, nothing to clean up - // this is Perf to reduce raising events unnecessarily. - if (changed) - { - // remove all diagnostics for the project - AnalyzerService.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - using var argsBuilder = TemporaryArray.Empty; - foreach (var stateSet in stateSets) - { - // clear all project diagnostics - AddDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), projectId, solution: null, stateSet.Analyzer); - } - - raiseEvents(argsBuilder.ToImmutableAndClear()); - }); - } - } - - return Task.CompletedTask; - } - - public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) - { - // let other components knows about this event - ClearCompilationsWithAnalyzersCache(); - - return Task.CompletedTask; - } -#endif - /// /// Return list of to be used for full solution analysis. /// @@ -413,20 +222,6 @@ private void RaiseProjectDiagnosticsIfNeeded( }); } -#if false - private void AddDocumentDiagnosticsArgsIfNeeded( - ref TemporaryArray builder, - TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray items) - => AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, [], items); - - private void AddDocumentDiagnosticsArgsIfNeeded( - ref TemporaryArray builder, - TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray oldItems, ImmutableArray newItems) - { - AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, oldItems, newItems, forceUpdate: false); - } -#endif - private void AddDocumentDiagnosticsArgsIfNeeded( ref TemporaryArray builder, TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, From 8df1119dc056d678c0c522a4e3767c2f7cbb922a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:34:20 -0800 Subject: [PATCH 116/151] simplify --- .../TaskList/ExternalErrorDiagnosticUpdateSource.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs index 11ddf74c2af7d..2630e0acf2a55 100644 --- a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -365,18 +365,6 @@ internal void OnSolutionBuildCompleted() return; } - // Explicitly start solution crawler if it didn't start yet. since solution crawler is lazy, - // user might have built solution before workspace fires its first event yet (which is when solution crawler is initialized) - // here we give initializeLazily: false so that solution crawler is fully initialized when we do de-dup live and build errors, - // otherwise, we will think none of error we have here belong to live errors since diagnostic service is not initialized yet. -#if false - if (_diagnosticService.GlobalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) - { - var registrationService = (SolutionCrawlerRegistrationService)_workspace.Services.GetRequiredService(); - registrationService.EnsureRegistration(_workspace, initializeLazily: false); - } -#endif - // Mark the status as updated to refresh error list before we invoke 'SyncBuildErrorsAndReportAsync', which can take some time to complete. OnBuildProgressChanged(inProgressState, BuildProgress.Updated); From f8ca81e946d706181975ebaaaf3870a3d4708999 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 12:55:10 -0800 Subject: [PATCH 117/151] lint --- .../Core.Wpf/Interactive/InteractiveEvaluator.cs | 2 +- .../Core.Wpf/Interactive/InteractiveWindowWorkspace.cs | 4 ++-- src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs | 4 +--- .../Test2/Diagnostics/DiagnosticServiceTests.vb | 1 - 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs index a83964cb395c5..a1e5eb6b4f882 100644 --- a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs +++ b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs @@ -84,7 +84,7 @@ internal CSharpInteractiveEvaluator( _commandsFactory = commandsFactory; _commands = commands; - _workspace = new InteractiveWindowWorkspace(hostServices, editorOptionsService.GlobalOptions); + _workspace = new InteractiveWindowWorkspace(hostServices); _session = new InteractiveSession( _workspace, diff --git a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowWorkspace.cs b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowWorkspace.cs index c322d33c9168e..3f0abbbdef375 100644 --- a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowWorkspace.cs +++ b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveWindowWorkspace.cs @@ -12,8 +12,8 @@ internal sealed class InteractiveWindowWorkspace : InteractiveWorkspace { public IInteractiveWindow? Window { get; set; } - public InteractiveWindowWorkspace(HostServices hostServices, IGlobalOptionService globalOptions) - : base(hostServices, globalOptions) + public InteractiveWindowWorkspace(HostServices hostServices) + : base(hostServices) { } } diff --git a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs index c34d534cc07a4..4a00dd65a66f7 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -16,7 +14,7 @@ internal partial class InteractiveWorkspace : Workspace private SourceTextContainer? _openTextContainer; private DocumentId? _openDocumentId; - internal InteractiveWorkspace(HostServices hostServices, IGlobalOptionService globalOptions) + internal InteractiveWorkspace(HostServices hostServices) : base(hostServices, WorkspaceKind.Interactive) { } diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 13b9317320c04..0427fb528af0a 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -2198,7 +2198,6 @@ class MyClass Assert.Equal(1, descriptors.Length) Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id) - ' Get cached project diagnostics. Dim diagnostics = Await diagnosticService.GetCachedDiagnosticsAsync(workspace, project.Id, documentId:=Nothing, includeSuppressedDiagnostics:=False, From 380ca90d7dff45aa17505733349224f6b94a2c7f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 13:41:30 -0800 Subject: [PATCH 118/151] Fix test --- .../Diagnostics/DiagnosticTaggerWrapper.cs | 1 - .../Squiggles/SquiggleUtilities.cs | 3 ++- .../Diagnostics/DiagnosticAnalyzerService.cs | 24 +++++++------------ ...sticAnalyzerService_IncrementalAnalyzer.cs | 5 +++- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index 781ade8d6577d..40c80229ce774 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -24,7 +24,6 @@ internal class DiagnosticTaggerWrapper where TTag : ITag { private readonly EditorTestWorkspace _workspace; - public readonly DiagnosticAnalyzerService? AnalyzerService; public readonly DiagnosticService DiagnosticService; private readonly IThreadingContext _threadingContext; private readonly IAsynchronousOperationListenerProvider _listenerProvider; diff --git a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs index efba61a1b8d2f..cfd5b23bac8ab 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs @@ -43,7 +43,8 @@ public static class SquiggleUtilities using var disposable = tagger as IDisposable; await wrapper.WaitForTags(); - var analyzerDiagnostics = await wrapper.AnalyzerService.GetDiagnosticsAsync(workspace.CurrentSolution, + var service = (DiagnosticAnalyzerService)workspace.ExportProvider.GetExportedValue(); + var analyzerDiagnostics = await service.GetDiagnosticsAsync(workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); var snapshot = textBuffer.CurrentSnapshot; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 03fdcd0317742..b66aa46999832 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -120,17 +120,13 @@ public Task> GetDiagnosticsForSpanAsync( bool isExplicit, CancellationToken cancellationToken) { - if (_map.TryGetValue(document.Project.Solution.Workspace, out var analyzer)) - { - priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); - - // always make sure that analyzer is called on background thread. - return Task.Run(() => analyzer.GetDiagnosticsForSpanAsync( - document, range, shouldIncludeDiagnostic, includeSuppressedDiagnostics, includeCompilerDiagnostics, - priorityProvider, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken), cancellationToken); - } + var analyzer = CreateIncrementalAnalyzer(document.Project.Solution.Workspace); + priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); - return SpecializedTasks.EmptyImmutableArray(); + // always make sure that analyzer is called on background thread. + return Task.Run(() => analyzer.GetDiagnosticsForSpanAsync( + document, range, shouldIncludeDiagnostic, includeSuppressedDiagnostics, includeCompilerDiagnostics, + priorityProvider, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken), cancellationToken); } public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) @@ -155,12 +151,8 @@ public Task> GetSpecificCachedDiagnosticsAsync(Wo public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { - if (_map.TryGetValue(solution.Workspace, out var analyzer)) - { - return analyzer.GetDiagnosticsAsync(solution, projectId, documentId, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); - } - - return SpecializedTasks.EmptyImmutableArray(); + var analyzer = CreateIncrementalAnalyzer(solution.Workspace); + return analyzer.GetDiagnosticsAsync(solution, projectId, documentId, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 7d1f9874d495f..7958f1b0589d9 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -17,7 +17,10 @@ namespace Microsoft.CodeAnalysis.Diagnostics; highPriorityForActiveFile: true)] internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider { - public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) + IIncrementalAnalyzer IIncrementalAnalyzerProvider.CreateIncrementalAnalyzer(Workspace workspace) + => CreateIncrementalAnalyzer(workspace); + + public DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) { return _map.GetValue(workspace, _createIncrementalAnalyzer); } From 8a0d58bda3940c328e580a2de267cf70cc2b47b7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 13:54:43 -0800 Subject: [PATCH 119/151] Fix test --- .../Test2/Diagnostics/DiagnosticProviderTests.vb | 10 +++++----- .../Features/Diagnostics/DiagnosticAnalyzerService.cs | 8 ++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index bfc4c8a47c2df..4768b8eab7245 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -269,11 +269,11 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Next Dim diagnosticProvider = GetDiagnosticProvider(workspace) - Dim actualDiagnostics = diagnosticProvider.GetCachedDiagnosticsAsync(workspace, projectId:=Nothing, documentId:=Nothing, - includeSuppressedDiagnostics:=False, - includeLocalDocumentDiagnostics:=True, - includeNonLocalDocumentDiagnostics:=True, - CancellationToken.None).Result + Dim actualDiagnostics = diagnosticProvider.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId:=Nothing, documentId:=Nothing, + includeSuppressedDiagnostics:=False, + includeNonLocalDocumentDiagnostics:=True, + CancellationToken.None).Result If diagnostics Is Nothing Then Assert.Equal(0, actualDiagnostics.Length) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index b66aa46999832..191172bb83021 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -131,12 +131,8 @@ public Task> GetDiagnosticsForSpanAsync( public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { - if (_map.TryGetValue(workspace, out var analyzer)) - { - return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); - } - - return SpecializedTasks.EmptyImmutableArray(); + var analyzer = CreateIncrementalAnalyzer(workspace); + return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } public Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) From 5d1b92c47afeac59e55812d0845b30043b44278b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 13:58:20 -0800 Subject: [PATCH 120/151] Fix test --- .../Features/Diagnostics/DiagnosticAnalyzerService.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 191172bb83021..7da24463d8081 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -162,12 +162,8 @@ public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken ca public Task> GetDiagnosticsForIdsAsync( Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { - if (_map.TryGetValue(solution.Workspace, out var analyzer)) - { - return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); - } - - return SpecializedTasks.EmptyImmutableArray(); + var analyzer = CreateIncrementalAnalyzer(solution.Workspace); + return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } public Task> GetProjectDiagnosticsForIdsAsync( From f2acaad67c3fd7deb6b3dc1184ac6217b29f44ae Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 14:03:14 -0800 Subject: [PATCH 121/151] Fix test --- .../Features/Diagnostics/DiagnosticAnalyzerService.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 7da24463d8081..28fe418604278 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -153,10 +153,8 @@ public Task> GetDiagnosticsAsync(Solution solutio public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { - if (_map.TryGetValue(project.Solution.Workspace, out var analyzer)) - { - await analyzer.ForceAnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); - } + var analyzer = CreateIncrementalAnalyzer(project.Solution.Workspace); + await analyzer.ForceAnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false); } public Task> GetDiagnosticsForIdsAsync( From beeaf7aa98c08bdf0af5c32c5bf22ad015c12ead Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 15:10:58 -0800 Subject: [PATCH 122/151] Always make anlayzers --- .../Diagnostics/DiagnosticAnalyzerService.cs | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 4c79658622274..507922c291017 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -89,23 +89,20 @@ public void Reanalyze(Workspace workspace, IEnumerable? projectIds, I bool isExplicit, CancellationToken cancellationToken) { - if (_map.TryGetValue(document.Project.Solution.Workspace, out var analyzer)) + var analyzer = CreateIncrementalAnalyzer(document.Project.Solution.Workspace); + + // always make sure that analyzer is called on background thread. + return Task.Run(async () => { - // always make sure that analyzer is called on background thread. - return Task.Run(async () => - { - priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); - - using var _ = ArrayBuilder.GetInstance(out var diagnostics); - var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( - document, range, diagnostics, shouldIncludeDiagnostic, - includeSuppressedDiagnostics, true, priorityProvider, blockForData: false, - addOperationScope: null, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); - return (diagnostics.ToImmutable(), upToDate); - }, cancellationToken); - } - - return Task.FromResult((ImmutableArray.Empty, upToDate: false)); + priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); + + using var _ = ArrayBuilder.GetInstance(out var diagnostics); + var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( + document, range, diagnostics, shouldIncludeDiagnostic, + includeSuppressedDiagnostics, true, priorityProvider, blockForData: false, + addOperationScope: null, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); + return (diagnostics.ToImmutable(), upToDate); + }, cancellationToken); } public Task> GetDiagnosticsForSpanAsync( @@ -159,12 +156,8 @@ public Task> GetProjectDiagnosticsForIdsAsync( Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { - if (_map.TryGetValue(solution.Workspace, out var analyzer)) - { - return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); - } - - return SpecializedTasks.EmptyImmutableArray(); + var analyzer = CreateIncrementalAnalyzer(solution.Workspace); + return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken); } } } From 292244a5cd93687a68c59e67b8a44c9a2f8821c7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 15:14:11 -0800 Subject: [PATCH 123/151] Remove explicit call to create analyzerS --- .../CSharpTest/Formatting/CodeCleanupTests.cs | 8 -------- src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs | 7 ------- .../VisualBasicTest/Formatting/CodeCleanUpTests.vb | 8 -------- .../HostWorkspace/VSCodeAnalyzerLoader.cs | 1 - src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs | 1 - 5 files changed, 25 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index 49570d696a435..ed4f45d17d1b9 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -830,10 +830,6 @@ private static async Task TestThirdPartyCodeFixer(string co project = project.AddAnalyzerConfigDocument(".editorconfig", SourceText.From(editorconfigText), filePath: @"z:\\.editorconfig").Project; workspace.TryApplyChanges(project.Solution); - // register this workspace to solution crawler so that analyzer service associate itself with given workspace - var incrementalAnalyzerProvider = workspace.ExportProvider.GetExportedValue() as IIncrementalAnalyzerProvider; - incrementalAnalyzerProvider.CreateIncrementalAnalyzer(workspace); - var hostdoc = workspace.Documents.Single(); var document = workspace.CurrentSolution.GetDocument(hostdoc.Id); @@ -931,10 +927,6 @@ private protected static async Task AssertCodeCleanupResult(string expected, str workspace.TryApplyChanges(solution); - // register this workspace to solution crawler so that analyzer service associate itself with given workspace - var incrementalAnalyzerProvider = workspace.ExportProvider.GetExportedValue() as IIncrementalAnalyzerProvider; - incrementalAnalyzerProvider.CreateIncrementalAnalyzer(workspace); - var hostdoc = workspace.Documents.Single(); var document = workspace.CurrentSolution.GetDocument(hostdoc.Id); diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 55b0c9716ad9c..4853c3f4c58a5 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -59,11 +59,6 @@ public async Task TestGetFirstDiagnosticWithFixAsync() var fixService = new CodeFixService( diagnosticService, logger, fixers, SpecializedCollections.EmptyEnumerable>()); - var incrementalAnalyzer = (IIncrementalAnalyzerProvider)diagnosticService; - - // register diagnostic engine to solution crawler - var analyzer = incrementalAnalyzer.CreateIncrementalAnalyzer(workspace); - var reference = new MockAnalyzerReference(); var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); var document = project.Documents.Single(); @@ -302,8 +297,6 @@ private static async Task> GetAddedFixesAsync( errorReportingService.OnError = message => errorReported = true; GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager); - var incrementalAnalyzer = (IIncrementalAnalyzerProvider)tuple.analyzerService; - var analyzer = incrementalAnalyzer.CreateIncrementalAnalyzer(workspace); var reference = new MockAnalyzerReference(codefix, ImmutableArray.Create(diagnosticAnalyzer)); var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); document = project.Documents.Single(); diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb index f88378f9d399a..fb093157fcbe5 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb @@ -528,10 +528,6 @@ End Class project = project.AddAnalyzerConfigDocument(".editorconfig", SourceText.From(editorconfigText), filePath:="z:\\.editorconfig").Project workspace.TryApplyChanges(project.Solution) - ' register this workspace to solution crawler so that analyzer service associate itself with given workspace - Dim incrementalAnalyzerProvider = TryCast(workspace.ExportProvider.GetExportedValue(Of IDiagnosticAnalyzerService)(), IIncrementalAnalyzerProvider) - incrementalAnalyzerProvider.CreateIncrementalAnalyzer(workspace) - Dim hostdoc = workspace.Documents.[Single]() Dim document = workspace.CurrentSolution.GetDocument(hostdoc.Id) @@ -580,10 +576,6 @@ End Class workspace.TryApplyChanges(solution) - ' register this workspace to solution crawler so that analyzer service associate itself with given workspace - Dim incrementalAnalyzerProvider = TryCast(workspace.ExportProvider.GetExportedValue(Of IDiagnosticAnalyzerService)(), IIncrementalAnalyzerProvider) - incrementalAnalyzerProvider.CreateIncrementalAnalyzer(workspace) - Dim hostdoc = workspace.Documents.[Single]() Dim document = workspace.CurrentSolution.GetDocument(hostdoc.Id) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs index 446bd3b4123b6..20e351e3691b1 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs @@ -30,7 +30,6 @@ public VSCodeAnalyzerLoader(IDiagnosticAnalyzerService analyzerService, IDiagnos public void InitializeDiagnosticsServices(Workspace workspace) { - _ = ((IIncrementalAnalyzerProvider)_analyzerService).CreateIncrementalAnalyzer(workspace); _diagnosticService.Register((IDiagnosticUpdateSource)_analyzerService); } diff --git a/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs b/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs index e52f9541d041f..402e7ceaa0be8 100644 --- a/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs +++ b/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs @@ -27,7 +27,6 @@ public RazorTestAnalyzerLoader(IDiagnosticAnalyzerService analyzerService, IDiag public void InitializeDiagnosticsServices(Workspace workspace) { - _ = ((IIncrementalAnalyzerProvider)_analyzerService).CreateIncrementalAnalyzer(workspace); _diagnosticService.Register((IDiagnosticUpdateSource)_analyzerService); } From a612d4384a370d776f551fc37b32d2dbc98c80bc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 15:16:42 -0800 Subject: [PATCH 124/151] Remove interfae method --- src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs | 9 +-------- .../DiagnosticAnalyzerService_IncrementalAnalyzer.cs | 3 --- src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs | 3 ++- .../SolutionCrawler/IIncrementalAnalyzerProvider.cs | 1 - 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 4853c3f4c58a5..6d4d10d2c0b9f 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -401,10 +401,8 @@ private static void GetDocumentAndExtensionManager( MockAnalyzerReference? analyzerReference = null, TextDocumentKind documentKind = TextDocumentKind.Document) { - var incrementalAnalyzer = (IIncrementalAnalyzerProvider)diagnosticService; - // register diagnostic engine to solution crawler - diagnosticIncrementalAnalyzer = (DiagnosticIncrementalAnalyzer)incrementalAnalyzer.CreateIncrementalAnalyzer(workspace)!; + diagnosticIncrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace)!; var reference = analyzerReference ?? new MockAnalyzerReference(); var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); @@ -787,11 +785,6 @@ private static async Task> GetNuGetAndVsixCode var fixService = new CodeFixService( diagnosticService, logger, vsixFixers, SpecializedCollections.EmptyEnumerable>()); - var incrementalAnalyzer = (IIncrementalAnalyzerProvider)diagnosticService; - - // register diagnostic engine to solution crawler - var analyzer = incrementalAnalyzer.CreateIncrementalAnalyzer(workspace); - diagnosticAnalyzer ??= new MockAnalyzerReference.MockDiagnosticAnalyzer(); var analyzers = ImmutableArray.Create(diagnosticAnalyzer); var reference = new MockAnalyzerReference(nugetFixer, analyzers); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 7958f1b0589d9..1d1ec93c2dbb2 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -17,9 +17,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics; highPriorityForActiveFile: true)] internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider { - IIncrementalAnalyzer IIncrementalAnalyzerProvider.CreateIncrementalAnalyzer(Workspace workspace) - => CreateIncrementalAnalyzer(workspace); - public DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) { return _map.GetValue(workspace, _createIncrementalAnalyzer); diff --git a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs index eea746fa40cab..834ffc5b54e72 100644 --- a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs +++ b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; @@ -66,7 +67,7 @@ public async Task RunAsync(CancellationToken cancellationToken) incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).SingleOrDefault(provider => provider.Metadata.WorkspaceKinds.Contains(WorkspaceKind.Host))?.Value; incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).SingleOrDefault(provider => provider.Metadata.WorkspaceKinds.Contains(WorkspaceKind.RemoteWorkspace))?.Value; incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).Single(provider => provider.Metadata.WorkspaceKinds is []).Value; - var incrementalAnalyzer = incrementalAnalyzerProvider.CreateIncrementalAnalyzer(_workspace); + ((DiagnosticAnalyzerService)incrementalAnalyzerProvider).CreateIncrementalAnalyzer(_workspace); } } } diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs index f44ded30a40fa..8e2e286827855 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs @@ -6,5 +6,4 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler; internal interface IIncrementalAnalyzerProvider { - IIncrementalAnalyzer? CreateIncrementalAnalyzer(Workspace workspace); } From 48281e1c65a9bd50cd442071766ca4578f3992f8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 15:25:04 -0800 Subject: [PATCH 125/151] remove incremental interfaces --- ...tingIncrementalAnalyzerProviderMetadata.cs | 5 ++-- ...sticAnalyzerService_IncrementalAnalyzer.cs | 6 +---- .../IncrementalAnalyzerRunner.cs | 10 -------- ...ortIncrementalAnalyzerProviderAttribute.cs | 20 ---------------- .../IIncrementalAnalyzerProvider.cs | 9 -------- .../IncrementalAnalyzerProviderMetadata.cs | 23 ------------------- .../WellKnownSolutionCrawlerAnalyzers.cs | 12 ---------- 7 files changed, 3 insertions(+), 82 deletions(-) delete mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/ExportIncrementalAnalyzerProviderAttribute.cs delete mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs delete mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs delete mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerProviderMetadata.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerProviderMetadata.cs index 3f96357c128f2..6f225fc7cec3d 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerProviderMetadata.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingIncrementalAnalyzerProviderMetadata.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler { @@ -13,8 +12,8 @@ internal sealed class UnitTestingIncrementalAnalyzerProviderMetadata(string name public IReadOnlyList WorkspaceKinds { get; } = workspaceKinds; public UnitTestingIncrementalAnalyzerProviderMetadata(IDictionary data) - : this(name: (string)data[nameof(CodeAnalysis.SolutionCrawler.ExportIncrementalAnalyzerProviderAttribute.Name)], - workspaceKinds: (IReadOnlyList)data[nameof(CodeAnalysis.SolutionCrawler.ExportIncrementalAnalyzerProviderAttribute.WorkspaceKinds)]) + : this(name: (string)data[nameof(Name)], + workspaceKinds: (IReadOnlyList)data[nameof(WorkspaceKinds)]) { } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 1d1ec93c2dbb2..1ff6c92d13603 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -11,11 +11,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics; -[ExportIncrementalAnalyzerProvider( - name: WellKnownSolutionCrawlerAnalyzers.Diagnostic, - workspaceKinds: [WorkspaceKind.Host, WorkspaceKind.Interactive], - highPriorityForActiveFile: true)] -internal partial class DiagnosticAnalyzerService : IIncrementalAnalyzerProvider +internal partial class DiagnosticAnalyzerService { public DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) { diff --git a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs index 834ffc5b54e72..400df749f61f7 100644 --- a/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs +++ b/src/Tools/AnalyzerRunner/IncrementalAnalyzerRunner.cs @@ -59,16 +59,6 @@ public async Task RunAsync(CancellationToken cancellationToken) throw new InvalidOperationException("Benchmark is not configured to use persistent storage."); } } - - var incrementalAnalyzerProviders = exportProvider.GetExports(); - foreach (var incrementalAnalyzerName in _options.IncrementalAnalyzerNames) - { - var incrementalAnalyzerProvider = incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).SingleOrDefault(provider => provider.Metadata.WorkspaceKinds.Contains(_workspace.Kind))?.Value; - incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).SingleOrDefault(provider => provider.Metadata.WorkspaceKinds.Contains(WorkspaceKind.Host))?.Value; - incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).SingleOrDefault(provider => provider.Metadata.WorkspaceKinds.Contains(WorkspaceKind.RemoteWorkspace))?.Value; - incrementalAnalyzerProvider ??= incrementalAnalyzerProviders.Where(x => x.Metadata.Name == incrementalAnalyzerName).Single(provider => provider.Metadata.WorkspaceKinds is []).Value; - ((DiagnosticAnalyzerService)incrementalAnalyzerProvider).CreateIncrementalAnalyzer(_workspace); - } } } } diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/ExportIncrementalAnalyzerProviderAttribute.cs b/src/Workspaces/Core/Portable/SolutionCrawler/ExportIncrementalAnalyzerProviderAttribute.cs deleted file mode 100644 index 20303ccbf2a10..0000000000000 --- a/src/Workspaces/Core/Portable/SolutionCrawler/ExportIncrementalAnalyzerProviderAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Composition; - -namespace Microsoft.CodeAnalysis.SolutionCrawler -{ - [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class)] - internal sealed class ExportIncrementalAnalyzerProviderAttribute(string name, string[] workspaceKinds, bool highPriorityForActiveFile = false) - : ExportAttribute(typeof(IIncrementalAnalyzerProvider)) - { - public bool HighPriorityForActiveFile { get; } = highPriorityForActiveFile; - public string Name { get; } = name; - public IReadOnlyList WorkspaceKinds { get; } = workspaceKinds; - } -} diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs deleted file mode 100644 index 8e2e286827855..0000000000000 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzerProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal interface IIncrementalAnalyzerProvider -{ -} diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs deleted file mode 100644 index bf315b9d63e43..0000000000000 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IncrementalAnalyzerProviderMetadata.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SolutionCrawler -{ - internal sealed class IncrementalAnalyzerProviderMetadata(string name, bool highPriorityForActiveFile, IReadOnlyList workspaceKinds) - { - public bool HighPriorityForActiveFile { get; } = highPriorityForActiveFile; - public string Name { get; } = name; - public IReadOnlyList WorkspaceKinds { get; } = workspaceKinds; - - public IncrementalAnalyzerProviderMetadata(IDictionary data) - : this(name: (string)data[nameof(ExportIncrementalAnalyzerProviderAttribute.Name)], - highPriorityForActiveFile: (bool)data[nameof(ExportIncrementalAnalyzerProviderAttribute.HighPriorityForActiveFile)], - workspaceKinds: (IReadOnlyList)data[nameof(ExportIncrementalAnalyzerProviderAttribute.WorkspaceKinds)]) - { - } - } -} diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs b/src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs deleted file mode 100644 index 719b559fbd344..0000000000000 --- a/src/Workspaces/Core/Portable/SolutionCrawler/WellKnownSolutionCrawlerAnalyzers.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal static class WellKnownSolutionCrawlerAnalyzers -{ - public const string Diagnostic = nameof(Diagnostic); -} From 9a4219a46ca3af57dff629c0823a16a5f5a2fa0d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 7 Mar 2024 15:28:44 -0800 Subject: [PATCH 126/151] Simplify --- .../DiagnosticAnalyzerServiceTests.cs | 22 +++++++++---------- ...sticAnalyzerService_IncrementalAnalyzer.cs | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index a8b551290776e..b84793ae3a2a5 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -80,7 +80,7 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var exportProvider = workspace.Services.SolutionServices.ExportProvider; Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var analyzer = service.CreateIncrementalAnalyzer(workspace); var globalOptions = exportProvider.GetExportedValue(); // listen to events @@ -204,7 +204,7 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab var exportProvider = workspace.Services.SolutionServices.ExportProvider; Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var analyzer = service.CreateIncrementalAnalyzer(workspace); // listen to events var syntaxDiagnostic = false; @@ -260,7 +260,7 @@ private static async Task TestAnalyzerAsync( var service = Assert.IsType(exportProvider.GetExportedValue()); var globalOptions = exportProvider.GetExportedValue(); - var analyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var analyzer = service.CreateIncrementalAnalyzer(workspace); var syntax = false; var semantic = false; @@ -449,7 +449,7 @@ public void TestHostAnalyzerOrdering() Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); } [Fact] @@ -504,7 +504,7 @@ public async Task TestHostAnalyzerErrorNotLeaking() } }; - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.True(called); @@ -611,7 +611,7 @@ private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace } }; - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(project.Solution.Workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(project.Solution.Workspace); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.Equal(expectAnalyzerExecuted, called); @@ -661,7 +661,7 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool diagnostics.AddRange(e.Diagnostics); }; - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); var firstAdditionalDocument = project.AdditionalDocuments.FirstOrDefault(); switch (analysisScope) @@ -776,7 +776,7 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS } }; - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); switch (analysisScope) { @@ -909,7 +909,7 @@ void M() } }; - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); switch (analysisScope) { @@ -1012,7 +1012,7 @@ void M() } }; - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); OpenDocumentAndMakeActive(document, workspace); @@ -1247,7 +1247,7 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, } }; - var incrementalAnalyzer = (DiagnosticIncrementalAnalyzer)service.CreateIncrementalAnalyzer(workspace); + var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); await incrementalAnalyzer.GetDiagnosticsAsync( workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 1ff6c92d13603..b52fa731ff259 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; From 0ba50f49a878609eb93108f0286a88feb83ad693 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 09:15:13 -0800 Subject: [PATCH 127/151] Fix test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index b84793ae3a2a5..7ae4740affe2b 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -505,6 +505,7 @@ public async Task TestHostAnalyzerErrorNotLeaking() }; var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.True(called); @@ -679,6 +680,7 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool throw ExceptionUtilities.UnexpectedValue(analysisScope); } + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); var expectedCount = (analysisScope, testMultiple) switch From d67d9b8a2e80fe92b9cd4e8eed8c526f2c9111dc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 09:29:04 -0800 Subject: [PATCH 128/151] Fix test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 7ae4740affe2b..903afcf5649dc 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -275,6 +275,7 @@ private static async Task TestAnalyzerAsync( } }; + await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); await analyzer.GetDiagnosticsAsync( workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); @@ -938,6 +939,7 @@ void M() break; } + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); var root = await document.GetSyntaxRootAsync(); @@ -1250,8 +1252,7 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, }; var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - await incrementalAnalyzer.GetDiagnosticsAsync( - workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); From 3919c904c30f3eaadc08c8ffe89f724a2752e1db Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 09:29:54 -0800 Subject: [PATCH 129/151] Fix test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 903afcf5649dc..137b6ece0e5db 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -614,6 +614,7 @@ private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace }; var incrementalAnalyzer = service.CreateIncrementalAnalyzer(project.Solution.Workspace); + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); Assert.Equal(expectAnalyzerExecuted, called); From d80f180e4269bedcfc8611581f8e6a327f5daf62 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 09:30:32 -0800 Subject: [PATCH 130/151] Fix test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 137b6ece0e5db..d2f7c74fe9fdb 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -236,8 +236,7 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab // open document workspace.OpenDocument(document.Id); - await analyzer.GetDiagnosticsAsync( - workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); + await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); // wait for all events to raised await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); From 108ad64d6930c3831e7000b2f3eb3d6920741a86 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 10:34:48 -0800 Subject: [PATCH 131/151] Fix test --- .../DiagnosticAnalyzerServiceTests.cs | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index d2f7c74fe9fdb..532a2b3cad95a 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -620,14 +620,13 @@ private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace } [Theory, CombinatorialData] - internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool testMultiple, BackgroundAnalysisScope analysisScope) + internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool testMultiple) { using var workspace = CreateWorkspace(); Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions( workspace.CurrentSolution.Options.WithChangedOption(new OptionKey(DiagnosticOptionsStorage.PullDiagnosticsFeatureFlag), false)))); var globalOptions = GetGlobalOptions(workspace); - globalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope); var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "CSharpProject", "CSharpProject", LanguageNames.CSharp); var project = workspace.AddProject(projectInfo); @@ -666,32 +665,12 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); var firstAdditionalDocument = project.AdditionalDocuments.FirstOrDefault(); - switch (analysisScope) - { - case BackgroundAnalysisScope.None: - case BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics: - case BackgroundAnalysisScope.OpenFiles: - workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); - break; - - case BackgroundAnalysisScope.FullSolution: - break; - - default: - throw ExceptionUtilities.UnexpectedValue(analysisScope); - } + workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); - var expectedCount = (analysisScope, testMultiple) switch - { - (BackgroundAnalysisScope.None or BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics, _) => 0, - (BackgroundAnalysisScope.OpenFiles or BackgroundAnalysisScope.FullSolution, false) => 1, - (BackgroundAnalysisScope.OpenFiles, true) => 2, - (BackgroundAnalysisScope.FullSolution, true) => 4, - _ => throw ExceptionUtilities.Unreachable(), - }; + var expectedCount = testMultiple ? 4 : 1; Assert.Equal(expectedCount, diagnostics.Count); @@ -704,21 +683,10 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool d => d.Id == analyzer.Descriptor.Id && d.DataLocation.UnmappedFileSpan.Path == additionalDoc.FilePath); var text = await additionalDoc.GetTextAsync(); - if (analysisScope is BackgroundAnalysisScope.None or BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics) - { - Assert.Empty(applicableDiagnostics); - } - else if (analysisScope == BackgroundAnalysisScope.OpenFiles && - firstAdditionalDocument != additionalDoc) - { - Assert.Empty(applicableDiagnostics); - } - else - { - var diagnostic = Assert.Single(applicableDiagnostics); - Assert.Equal(diagnosticSpan, diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)); - diagnostics.Remove(diagnostic); - } + + var diagnostic = Assert.Single(applicableDiagnostics); + Assert.Equal(diagnosticSpan, diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)); + diagnostics.Remove(diagnostic); } } From 2f8e4e38b4234fedcaf3393457d4261c75e629e0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 10:37:06 -0800 Subject: [PATCH 132/151] Fix test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 532a2b3cad95a..2815d77b64b80 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -1005,8 +1005,7 @@ await incrementalAnalyzer.GetDiagnosticsAsync( Assert.Null(diagnostic); // Then invoke analysis without cancellation token, and verify non-cancelled diagnostic. - await incrementalAnalyzer.GetDiagnosticsAsync( - workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); From 615fc8efe8be09b80dd9a4be78db6b91654c4b6b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 10:39:06 -0800 Subject: [PATCH 133/151] Fix test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 2815d77b64b80..e41058486999e 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -769,9 +769,10 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS throw ExceptionUtilities.UnexpectedValue(analysisScope); } + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); - if (includeAnalyzer && analysisScope != BackgroundAnalysisScope.None) + if (includeAnalyzer) { Assert.True(diagnostic != null); Assert.Equal(NamedTypeAnalyzer.DiagnosticId, diagnostic.Id); From 26a63e28c682bdaa89d883152d0bd1a9624a7cc3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 10:40:59 -0800 Subject: [PATCH 134/151] Fix test --- .../DiagnosticAnalyzerServiceTests.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index e41058486999e..e68c145368c8c 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -913,28 +913,21 @@ void M() var root = await document.GetSyntaxRootAsync(); text = await document.GetTextAsync(); - if (analysisScope == BackgroundAnalysisScope.None) + + Assert.Equal(2, diagnostics.Count); + if (testPragma) { - // Anayzers are disabled for BackgroundAnalysisScope.None. - Assert.Empty(diagnostics); + var pragma1 = root.FindTrivia(diagnostics[0].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToString(); + Assert.Equal($"#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Unnecessary", pragma1); + var pragma2 = root.FindTrivia(diagnostics[1].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToString(); + Assert.Equal($"#pragma warning disable CS0168 // Variable is declared but never used - Unnecessary", pragma2); } else { - Assert.Equal(2, diagnostics.Count); - if (testPragma) - { - var pragma1 = root.FindTrivia(diagnostics[0].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToString(); - Assert.Equal($"#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Unnecessary", pragma1); - var pragma2 = root.FindTrivia(diagnostics[1].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToString(); - Assert.Equal($"#pragma warning disable CS0168 // Variable is declared but never used - Unnecessary", pragma2); - } - else - { - var attribute1 = root.FindNode(diagnostics[0].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)).ToString(); - Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category2"", ""{NamedTypeAnalyzer.DiagnosticId}"")", attribute1); - var attribute2 = root.FindNode(diagnostics[1].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)).ToString(); - Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category3"", ""CS0168"")", attribute2); - } + var attribute1 = root.FindNode(diagnostics[0].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)).ToString(); + Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category2"", ""{NamedTypeAnalyzer.DiagnosticId}"")", attribute1); + var attribute2 = root.FindNode(diagnostics[1].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)).ToString(); + Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category3"", ""CS0168"")", attribute2); } } From 50b59b64f872d35ff82ebacd51912d5f9027833d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 11:06:03 -0800 Subject: [PATCH 135/151] REvert --- src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 6d4d10d2c0b9f..fd1a75c537cd0 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1060,6 +1060,7 @@ void M() // We enable full solution analysis so the 'AnalyzeDocumentAsync' doesn't skip analysis based on whether the document is active/open. workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); + await diagnosticIncrementalAnalyzer.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnosticIncrementalAnalyzer); // Compute and apply code edit @@ -1091,6 +1092,7 @@ void M() if (testWithCachedDiagnostics) { + await diagnosticIncrementalAnalyzer.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: !expectedNoFixes, testSpan, diagnosticIncrementalAnalyzer); } From 5859b291bb9a4d0564a32429364486a817dcc6b6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:06:53 -0800 Subject: [PATCH 136/151] Fix test --- .../Test/CodeFixes/CodeFixServiceTests.cs | 20 ++++++------------ ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index fd1a75c537cd0..5cbd1d2fad9ce 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1008,7 +1008,6 @@ public AdditionalFileFixerWithoutDocumentKindsAndExtensions() : base(nameof(Addi [Theory, CombinatorialData] public async Task TestGetFixesWithDeprioritizedAnalyzerAsync( DeprioritizedAnalyzer.ActionKind actionKind, - bool testWithCachedDiagnostics, bool diagnosticOnFixLineInPriorSnapshot, bool editOnFixLine, bool addNewLineWithEdit) @@ -1028,7 +1027,7 @@ public async Task TestGetFixesWithDeprioritizedAnalyzerAsync( // of the heuristic where we compare intersecting diagnostics across document snapshots only if both // snapshots have the same number of lines. - var expectDeprioritization = GetExpectDeprioritization(actionKind, testWithCachedDiagnostics, diagnosticOnFixLineInPriorSnapshot, addNewLineWithEdit); + var expectDeprioritization = GetExpectDeprioritization(actionKind, diagnosticOnFixLineInPriorSnapshot, addNewLineWithEdit); var priorSnapshotFixLine = diagnosticOnFixLineInPriorSnapshot ? "int x1 = 0;" : "System.Console.WriteLine();"; var code = $@" @@ -1090,11 +1089,9 @@ void M() ? root.DescendantNodes().OfType().First().Span : root.DescendantNodes().OfType().First().Span; - if (testWithCachedDiagnostics) - { - await diagnosticIncrementalAnalyzer.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); - await VerifyCachedDiagnosticsAsync(sourceDocument, expectedCachedDiagnostic: !expectedNoFixes, testSpan, diagnosticIncrementalAnalyzer); - } + await diagnosticIncrementalAnalyzer.GetDiagnosticsAsync( + sourceDocument.Project.Solution, sourceDocument.Project.Id, sourceDocument.Id, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); + await diagnosticIncrementalAnalyzer.GetTestAccessor().TextDocumentOpenAsync(sourceDocument); var lowPriorityAnalyzers = new ConcurrentSet(); var priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Default, lowPriorityAnalyzers); @@ -1130,7 +1127,6 @@ void M() static bool GetExpectDeprioritization( DeprioritizedAnalyzer.ActionKind actionKind, - bool testWithCachedDiagnostics, bool diagnosticOnFixLineInPriorSnapshot, bool addNewLineWithEdit) { @@ -1141,11 +1137,6 @@ static bool GetExpectDeprioritization( // a. We do not have an analyzer diagnostic reported in the prior document snapshot on the edited line OR // b. Number of lines in the prior document snapshot differs from number of lines in the current document snapshot, // i.e. we added a new line with the edit and 'addNewLineWithEdit = true'. - - // Condition 1 - if (testWithCachedDiagnostics) - return false; - // Condition 2 if (actionKind is not (DeprioritizedAnalyzer.ActionKind.SymbolStartEnd or DeprioritizedAnalyzer.ActionKind.SemanticModel)) return false; @@ -1154,7 +1145,8 @@ static bool GetExpectDeprioritization( if (!diagnosticOnFixLineInPriorSnapshot) return true; - // Condition 3(b) + //return true; + //// Condition 3(b) return addNewLineWithEdit; } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 451b730994044..187f528b30431 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -74,6 +74,18 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C } } + private async Task TextDocumentOpenAsync(TextDocument document, CancellationToken cancellationToken) + { + using (Logger.LogBlock(FunctionId.Diagnostics_DocumentOpen, GetOpenLogMessage, document, cancellationToken)) + { + var stateSets = _stateManager.GetStateSets(document.Project); + + // can not be canceled + foreach (var stateSet in stateSets) + await stateSet.OnDocumentOpenedAsync(document).ConfigureAwait(false); + } + } + /// /// Return list of to be used for full solution analysis. /// @@ -322,5 +334,14 @@ private void AddProjectDiagnosticsRemovedArgs(ref TemporaryArray new(this); + + public readonly struct TestAccessor(DiagnosticIncrementalAnalyzer diagnosticIncrementalAnalyzer) + { + public Task TextDocumentOpenAsync(TextDocument document) + => diagnosticIncrementalAnalyzer.TextDocumentOpenAsync(document, CancellationToken.None); + } } } From 5bcb0c958ad0738f18aca739ce19f5270296928f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:11:54 -0800 Subject: [PATCH 137/151] update --- .../Test/CodeFixes/CodeFixServiceTests.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 5cbd1d2fad9ce..3e75fd87eb280 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -1131,22 +1131,21 @@ static bool GetExpectDeprioritization( bool addNewLineWithEdit) { // We expect de-prioritization of analyzer from 'Normal' to 'Low' bucket only if following conditions are met: - // 1. There are no cached diagnostics from background analysis, i.e. 'testWithCachedDiagnostics == false'. - // 2. We have an expensive analyzer that registers SymbolStart/End or SemanticModel actions, both of which have a broad analysis scope. - // 3. Either of the below is true: + // 1. We have an expensive analyzer that registers SymbolStart/End or SemanticModel actions, both of which have a broad analysis scope. + // 2. Either of the below is true: // a. We do not have an analyzer diagnostic reported in the prior document snapshot on the edited line OR // b. Number of lines in the prior document snapshot differs from number of lines in the current document snapshot, // i.e. we added a new line with the edit and 'addNewLineWithEdit = true'. - // Condition 2 + + // Condition 1 if (actionKind is not (DeprioritizedAnalyzer.ActionKind.SymbolStartEnd or DeprioritizedAnalyzer.ActionKind.SemanticModel)) return false; - // Condition 3(a) + // Condition 2(a) if (!diagnosticOnFixLineInPriorSnapshot) return true; - //return true; - //// Condition 3(b) + // Condition 2(b) return addNewLineWithEdit; } From cd055f2dbf8287dd0f6f3688664b0064becafc79 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:13:48 -0800 Subject: [PATCH 138/151] Remove --- ...gnosticIncrementalAnalyzer.StateManager.cs | 12 ----- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 52 ------------------- 2 files changed, 64 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index d3b1751751e4d..f2b5d7ecde91e 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -191,18 +191,6 @@ public ImmutableArray CreateBuildOnlyProjectStateSet(Project project) return stateSets.ToImmutable(); } - public bool OnProjectRemoved(IEnumerable stateSets, ProjectId projectId) - { - var removed = false; - foreach (var stateSet in stateSets) - { - removed |= stateSet.OnProjectRemoved(projectId); - } - - _projectAnalyzerStateMap.TryRemove(projectId, out _); - return removed; - } - private void RaiseProjectAnalyzerReferenceChanged(ProjectAnalyzerReferenceChangedEventArgs args) => ProjectAnalyzerReferenceChanged?.Invoke(this, args); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 851898e113e0c..c9c03dbdb970b 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -126,58 +126,6 @@ public async Task OnDocumentOpenedAsync(TextDocument document) return true; } - public async Task OnDocumentClosedAsync(TextDocument document, IGlobalOptionService globalOptions) - { - // can not be cancelled - // remove active file state and put it in project state - if (!_activeFileStates.TryRemove(document.Id, out var activeFileState)) - { - return false; - } - - // active file exist, put it in the project state - var projectState = GetOrCreateProjectState(document.Project.Id); - await projectState.MergeAsync(activeFileState, document, globalOptions).ConfigureAwait(false); - return true; - } - - public bool OnDocumentReset(TextDocument document) - { - var changed = false; - // can not be cancelled - // remove active file state and put it in project state - if (TryGetActiveFileState(document.Id, out var activeFileState)) - { - activeFileState.ResetVersion(); - changed |= true; - } - - if (TryGetProjectState(document.Project.Id, out var projectState)) - { - projectState.ResetVersion(); - changed |= true; - } - - return changed; - } - - public bool OnDocumentRemoved(DocumentId id) - { - // remove active file state for removed document - var removed = false; - if (_activeFileStates.TryRemove(id, out _)) - { - removed = true; - } - // remove state for the file that got removed. - if (_projectStates.TryGetValue(id.ProjectId, out var state)) - { - removed |= state.OnDocumentRemoved(id); - } - - return removed; - } - public bool OnProjectRemoved(ProjectId id) { // remove state for project that got removed. From e023df2a5d1a6b82392726da562d1128cbc48835 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:18:48 -0800 Subject: [PATCH 139/151] Docs --- .../HostLegacySolutionEventsWorkspaceEventListener.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs index 696513ff218eb..eb078261a4b36 100644 --- a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -11,14 +11,17 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LegacySolutionEvents; +/// +/// Event listener that hears about workspaces and exists solely to let unit testing continue to work using their own +/// fork of solution crawler. Importantly, this is always active until the point that we can get unit testing to move +/// to an entirely differently (ideally 'pull') model for test discovery. +/// [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] internal sealed partial class HostLegacySolutionEventsWorkspaceEventListener : IEventListener { From 6ed1ed6c109fd351ff93684b8dd5eefe6fc19f23 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:25:18 -0800 Subject: [PATCH 140/151] Fix test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index e68c145368c8c..f82d69c2dd426 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -986,8 +986,7 @@ void M() Assert.Empty(analyzer.CanceledCompilations); try { - await incrementalAnalyzer.GetDiagnosticsAsync( - workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, analyzer.CancellationToken); + await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, analyzer.CancellationToken); throw ExceptionUtilities.Unreachable(); } From 07de2181c0adf94071c041653bf63e3d68d58310 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:28:23 -0800 Subject: [PATCH 141/151] Simplify --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index f82d69c2dd426..d63f1d51e7643 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -252,7 +252,6 @@ private static async Task TestAnalyzerAsync( Func, (bool, bool)> resultSetter, bool expectedSyntax, bool expectedSemantic) { - _ = document; var exportProvider = workspace.Services.SolutionServices.ExportProvider; Assert.IsType(exportProvider.GetExportedValue()); @@ -275,8 +274,6 @@ private static async Task TestAnalyzerAsync( }; await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); - await analyzer.GetDiagnosticsAsync( - workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); // wait for all events to raised await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); From 6c0039502cf74468c417bb40f901e4565d5fb6bf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:44:23 -0800 Subject: [PATCH 142/151] Add back --- .../DiagnosticIncrementalAnalyzer.StateManager.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index f2b5d7ecde91e..d3b1751751e4d 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -191,6 +191,18 @@ public ImmutableArray CreateBuildOnlyProjectStateSet(Project project) return stateSets.ToImmutable(); } + public bool OnProjectRemoved(IEnumerable stateSets, ProjectId projectId) + { + var removed = false; + foreach (var stateSet in stateSets) + { + removed |= stateSet.OnProjectRemoved(projectId); + } + + _projectAnalyzerStateMap.TryRemove(projectId, out _); + return removed; + } + private void RaiseProjectAnalyzerReferenceChanged(ProjectAnalyzerReferenceChangedEventArgs args) => ProjectAnalyzerReferenceChanged?.Invoke(this, args); From 8bb165e43b451704f74b535a1799021cc8453464 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:45:10 -0800 Subject: [PATCH 143/151] Add back --- .../DiagnosticIncrementalAnalyzer.StateSet.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index c9c03dbdb970b..851898e113e0c 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -126,6 +126,58 @@ public async Task OnDocumentOpenedAsync(TextDocument document) return true; } + public async Task OnDocumentClosedAsync(TextDocument document, IGlobalOptionService globalOptions) + { + // can not be cancelled + // remove active file state and put it in project state + if (!_activeFileStates.TryRemove(document.Id, out var activeFileState)) + { + return false; + } + + // active file exist, put it in the project state + var projectState = GetOrCreateProjectState(document.Project.Id); + await projectState.MergeAsync(activeFileState, document, globalOptions).ConfigureAwait(false); + return true; + } + + public bool OnDocumentReset(TextDocument document) + { + var changed = false; + // can not be cancelled + // remove active file state and put it in project state + if (TryGetActiveFileState(document.Id, out var activeFileState)) + { + activeFileState.ResetVersion(); + changed |= true; + } + + if (TryGetProjectState(document.Project.Id, out var projectState)) + { + projectState.ResetVersion(); + changed |= true; + } + + return changed; + } + + public bool OnDocumentRemoved(DocumentId id) + { + // remove active file state for removed document + var removed = false; + if (_activeFileStates.TryRemove(id, out _)) + { + removed = true; + } + // remove state for the file that got removed. + if (_projectStates.TryGetValue(id.ProjectId, out var state)) + { + removed |= state.OnDocumentRemoved(id); + } + + return removed; + } + public bool OnProjectRemoved(ProjectId id) { // remove state for project that got removed. From 71e22da7f207e1875ac973d9c0aae583aebc304e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 12:49:23 -0800 Subject: [PATCH 144/151] remove interface --- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 2 +- src/Tools/AnalyzerRunner/Program.cs | 2 +- .../SolutionCrawler/IIncrementalAnalyzer.cs | 15 --------------- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 3b430981ab6b3..7fabe03ff49f0 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 /// /// This one follows pattern compiler has set for diagnostic analyzer. /// - internal partial class DiagnosticIncrementalAnalyzer : IIncrementalAnalyzer + internal partial class DiagnosticIncrementalAnalyzer { private readonly int _correlationId; private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); diff --git a/src/Tools/AnalyzerRunner/Program.cs b/src/Tools/AnalyzerRunner/Program.cs index 4e3a0170bf58e..c0e3fa7e1845e 100644 --- a/src/Tools/AnalyzerRunner/Program.cs +++ b/src/Tools/AnalyzerRunner/Program.cs @@ -106,7 +106,7 @@ public static async Task Main(string[] args) { if (!string.IsNullOrEmpty(options.ProfileRoot)) { - ProfileOptimization.StartProfile(nameof(Microsoft.CodeAnalysis.SolutionCrawler.IIncrementalAnalyzer)); + ProfileOptimization.StartProfile("IncrementalAnalyzer"); } await incrementalAnalyzerRunner.RunAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs b/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs deleted file mode 100644 index b3a7868333f5c..0000000000000 --- a/src/Workspaces/Core/Portable/SolutionCrawler/IIncrementalAnalyzer.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.SolutionCrawler; - -internal interface IIncrementalAnalyzer -{ -} From d5b3e94085106fc11fb69f9cdb66284969bd91e1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 8 Mar 2024 13:19:04 -0800 Subject: [PATCH 145/151] lint --- src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs b/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs index 402e7ceaa0be8..b27bd3359fed1 100644 --- a/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs +++ b/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs @@ -25,7 +25,9 @@ public RazorTestAnalyzerLoader(IDiagnosticAnalyzerService analyzerService, IDiag _diagnosticService = (DiagnosticService)diagnosticService; } +#pragma warning disable IDE0060 // Remove unused parameter public void InitializeDiagnosticsServices(Workspace workspace) +#pragma warning restore IDE0060 // Remove unused parameter { _diagnosticService.Register((IDiagnosticUpdateSource)_analyzerService); } From 9e7686cfd072d775d2cec5ded6abb58b2ea07290 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 9 Mar 2024 10:04:16 -0800 Subject: [PATCH 146/151] Remove parameter --- .../HostWorkspace/LanguageServerWorkspaceFactory.cs | 2 +- .../HostWorkspace/VSCodeAnalyzerLoader.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs index 3e397764fc809..78c3e4dc30378 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs @@ -38,7 +38,7 @@ public LanguageServerWorkspaceFactory( ProjectSystemProjectFactory = new ProjectSystemProjectFactory(Workspace, fileChangeWatcher, static (_, _) => Task.CompletedTask, _ => { }); workspace.ProjectSystemProjectFactory = ProjectSystemProjectFactory; - analyzerLoader.InitializeDiagnosticsServices(Workspace); + analyzerLoader.InitializeDiagnosticsServices(); ProjectSystemHostInfo = new ProjectSystemHostInfo( DynamicFileInfoProviders: dynamicFileInfoProviders.ToImmutableArray(), diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs index 20e351e3691b1..81ad2a04ee55d 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs @@ -28,7 +28,7 @@ public VSCodeAnalyzerLoader(IDiagnosticAnalyzerService analyzerService, IDiagnos _diagnosticService = (DiagnosticService)diagnosticService; } - public void InitializeDiagnosticsServices(Workspace workspace) + public void InitializeDiagnosticsServices() { _diagnosticService.Register((IDiagnosticUpdateSource)_analyzerService); } From 83b1946cfbfa156500e78be876c3102b771ea67b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 9 Mar 2024 10:13:17 -0800 Subject: [PATCH 147/151] Fix tet --- src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 669a1e31877f5..5fc2bdf6bb0cb 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -372,7 +372,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_enable_smart_indenter", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Smart Indenter")}, {"dotnet_enable_snippets", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Snippets2")}, {"dotnet_enable_syntactic_colorizer", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Syntactic Colorizer")}, - {"dotnet_enable_solution_crawler", new LocalUserProfileStorage(@"Roslyn\Internal\SolutionCrawler", "Solution Crawler")}, {"dotnet_colorize_json_patterns", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ColorizeJsonPatterns")}, {"dotnet_detect_and_offer_editor_features_for_probable_json_strings", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.DetectAndOfferEditorFeaturesForProbableJsonStrings")}, {"dotnet_highlight_related_json_components", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.HighlightRelatedJsonComponentsUnderCursor")}, From 5b866f3bb5a85af482ad64a2431d6a97f97d372e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Mar 2024 05:58:27 -0700 Subject: [PATCH 148/151] Restore test --- .../Diagnostics/DiagnosticAnalyzerServiceTests.cs | 14 ++++++++++++++ .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index d63f1d51e7643..a366b436a54c9 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -447,6 +447,20 @@ public void TestHostAnalyzerOrdering() var service = Assert.IsType(exportProvider.GetExportedValue()); var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); + var analyzers = incrementalAnalyzer.GetAnalyzersTestOnly(project).ToArray(); + + AssertEx.Equal(new[] + { + typeof(FileContentLoadAnalyzer), + typeof(GeneratorDiagnosticsPlaceholderAnalyzer), + typeof(CSharpCompilerDiagnosticAnalyzer), + typeof(Analyzer), + typeof(Priority0Analyzer), + typeof(Priority1Analyzer), + typeof(Priority10Analyzer), + typeof(Priority15Analyzer), + typeof(Priority20Analyzer) + }, analyzers.Select(a => a.GetType())); } [Fact] diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 7fabe03ff49f0..3bfa3692191b2 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -190,6 +191,9 @@ private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary GetAnalyzersTestOnly(Project project) + => _stateManager.GetOrCreateStateSets(project).Select(s => s.Analyzer); + private static string GetDocumentLogMessage(string title, TextDocument document, DiagnosticAnalyzer analyzer) => $"{title}: ({document.Id}, {document.Project.Id}), ({analyzer})"; From d53b35dda0386857ff1a5bbca0835638b9d1f56d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Mar 2024 06:10:45 -0700 Subject: [PATCH 149/151] Add back option --- ...LegacySolutionEventsWorkspaceEventListener.cs | 16 ++++++++++++---- .../SolutionCrawler/SolutionCrawlerService.cs | 12 ++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs index eb078261a4b36..15e0c524f47b8 100644 --- a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -11,8 +11,10 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LegacySolutionEvents; @@ -25,15 +27,18 @@ namespace Microsoft.CodeAnalysis.LegacySolutionEvents; [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] internal sealed partial class HostLegacySolutionEventsWorkspaceEventListener : IEventListener { + private readonly IGlobalOptionService _globalOptions; private readonly IThreadingContext _threadingContext; private readonly AsyncBatchingWorkQueue _eventQueue; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public HostLegacySolutionEventsWorkspaceEventListener( + IGlobalOptionService globalOptions, IThreadingContext threadingContext, IAsynchronousOperationListenerProvider listenerProvider) { + _globalOptions = globalOptions; _threadingContext = threadingContext; _eventQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.Short, @@ -44,11 +49,14 @@ public HostLegacySolutionEventsWorkspaceEventListener( public void StartListening(Workspace workspace, object? serviceOpt) { - workspace.WorkspaceChanged += OnWorkspaceChanged; - _threadingContext.DisposalToken.Register(() => + if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { - workspace.WorkspaceChanged -= OnWorkspaceChanged; - }); + workspace.WorkspaceChanged += OnWorkspaceChanged; + _threadingContext.DisposalToken.Register(() => + { + workspace.WorkspaceChanged -= OnWorkspaceChanged; + }); + } } private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs new file mode 100644 index 0000000000000..12a64f92dfa8b --- /dev/null +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.SolutionCrawler; + +internal partial class SolutionCrawlerRegistrationService +{ + internal static readonly Option2 EnableSolutionCrawler = new("dotnet_enable_solution_crawler", defaultValue: true); +} From 02077d17eb9a70bc662f9a488a918cc9aac30a2b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Mar 2024 06:11:16 -0700 Subject: [PATCH 150/151] Add back option --- .../HostLegacySolutionEventsWorkspaceEventListener.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs index 15e0c524f47b8..9c34bcc14bbca 100644 --- a/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs +++ b/src/EditorFeatures/Core/SolutionEvents/HostLegacySolutionEventsWorkspaceEventListener.cs @@ -49,6 +49,8 @@ public HostLegacySolutionEventsWorkspaceEventListener( public void StartListening(Workspace workspace, object? serviceOpt) { + // We only support this option to disable crawling in internal perf runs to lower noise. It is not exposed to + // the user. if (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler)) { workspace.WorkspaceChanged += OnWorkspaceChanged; From f9899b3b71bafead38ff4d92d6f57c30e881dd3b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Mar 2024 07:26:16 -0700 Subject: [PATCH 151/151] Fix option --- src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 5fc2bdf6bb0cb..669a1e31877f5 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -372,6 +372,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_enable_smart_indenter", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Smart Indenter")}, {"dotnet_enable_snippets", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Snippets2")}, {"dotnet_enable_syntactic_colorizer", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Syntactic Colorizer")}, + {"dotnet_enable_solution_crawler", new LocalUserProfileStorage(@"Roslyn\Internal\SolutionCrawler", "Solution Crawler")}, {"dotnet_colorize_json_patterns", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ColorizeJsonPatterns")}, {"dotnet_detect_and_offer_editor_features_for_probable_json_strings", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.DetectAndOfferEditorFeaturesForProbableJsonStrings")}, {"dotnet_highlight_related_json_components", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.HighlightRelatedJsonComponentsUnderCursor")},