From 4be55f5fec5da14555022ae410ced7f84009ee4b Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 17 Dec 2021 12:22:49 -0800 Subject: [PATCH] Run solution crawler on source generated documents that change --- .../AbstractLanguageServerProtocolTests.cs | 8 +++- .../SolutionCrawler/WorkCoordinator.cs | 38 +++++++++++++++++++ .../Options/AdvancedOptionPageControl.xaml.cs | 4 +- .../Workspace/SourceGeneratedFileManager.cs | 29 +------------- .../VisualStudioWorkspace_OutOfProc.cs | 4 +- .../Options/AdvancedOptionPageControl.xaml.vb | 4 +- .../WorkspaceConfigurationOptions.cs | 20 +++++++++- 7 files changed, 70 insertions(+), 37 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 2c46002bc4acf..301571a71db5e 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -299,10 +299,14 @@ protected Task CreateTestLspServerAsync(string[] markups, string[ private Task CreateTestLspServerAsync(string[] markups, string[] sourceGeneratedMarkups, string languageName) { + var exportProvider = Composition.ExportProviderFactory.CreateExportProvider(); + var globalOptions = exportProvider.GetExportedValue(); + globalOptions.SetGlobalOption(new OptionKey(WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace), true); + var workspace = languageName switch { - LanguageNames.CSharp => TestWorkspace.CreateCSharp(markups, sourceGeneratedMarkups, composition: Composition), - LanguageNames.VisualBasic => TestWorkspace.CreateVisualBasic(markups, sourceGeneratedMarkups, composition: Composition), + LanguageNames.CSharp => TestWorkspace.CreateCSharp(markups, sourceGeneratedMarkups, exportProvider: exportProvider), + LanguageNames.VisualBasic => TestWorkspace.CreateVisualBasic(markups, sourceGeneratedMarkups, exportProvider: exportProvider), _ => throw new ArgumentException($"language name {languageName} is not valid for a test workspace"), }; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 1fdfa9c7f61f3..35c1f597d91be 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Roslyn.Utilities; @@ -434,6 +435,37 @@ private void EnqueueDocumentChangedEvent(Solution oldSolution, Solution newSolut var newProject = newSolution.GetRequiredProject(documentId.ProjectId); await EnqueueChangedDocumentWorkItemAsync(oldProject.GetRequiredDocument(documentId), newProject.GetRequiredDocument(documentId)).ConfigureAwait(false); + + if (WorkspaceConfigurationOptions.ShouldConnectSourceGeneratedFilesToWorkspace(newSolution.Options)) + { + 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); } @@ -487,6 +519,12 @@ private async Task EnqueueFullProjectWorkItemAsync(Project project, InvocationRe foreach (var documentId in project.AnalyzerConfigDocumentIds) await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); + + if (WorkspaceConfigurationOptions.ShouldConnectSourceGeneratedFilesToWorkspace(project.Solution.Options)) + { + 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) diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index f769d0fbfbe3a..3a8cbb6d81170 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -109,11 +109,11 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(RenameTrackingPreview, FeatureOnOffOptions.RenameTrackingPreview, LanguageNames.CSharp); BindToOption(Underline_reassigned_variables, ClassificationOptions.Metadata.ClassifyReassignedVariables, LanguageNames.CSharp); - BindToOption(Enable_all_features_in_opened_files_from_source_generators, SourceGeneratedFileManager.Options.EnableOpeningInWorkspace, () => + BindToOption(Enable_all_features_in_opened_files_from_source_generators, WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace, () => { // If the option has not been set by the user, check if the option is enabled from experimentation. // If so, default to that. - return optionStore.GetOption(SourceGeneratedFileManager.Options.EnableOpeningInWorkspaceFeatureFlag); + return optionStore.GetOption(WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag); }); BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptions.DontPutOutOrRefOnStruct, LanguageNames.CSharp); diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs index 8e69f8a199f03..74889642a0454 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs @@ -394,8 +394,7 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) // If the file isn't already open, open it now. We may transition between opening and closing // if the file is repeatedly appearing and disappearing. - var connectToWorkspace = _workspace.Options.GetOption(Options.EnableOpeningInWorkspace) ?? - _workspace.Options.GetOption(Options.EnableOpeningInWorkspaceFeatureFlag); + var connectToWorkspace = WorkspaceConfigurationOptions.ShouldConnectSourceGeneratedFilesToWorkspace(_workspace.Options); if (connectToWorkspace && !_workspace.IsDocumentOpen(_documentIdentity.DocumentId)) { @@ -505,31 +504,5 @@ public void NavigateToSpan(TextSpan sourceSpan, CancellationToken cancellationTo _fileManager._visualStudioDocumentNavigationService.NavigateTo(_textBuffer, sourceText.GetVsTextSpanForSpan(sourceSpan), cancellationToken); } } - - [Export(typeof(IOptionProvider))] - internal sealed class Options : IOptionProvider - { - private const string FeatureName = "SourceGeneratedFileManager"; - - /// - /// This option allows the user to enable this. We are putting this behind a feature flag for now since we could have extensions - /// surprised by this and we want some time to work through those issues. - /// - internal static readonly Option2 EnableOpeningInWorkspace = new(FeatureName, nameof(EnableOpeningInWorkspace), defaultValue: null, - new RoamingProfileStorageLocation("TextEditor.Roslyn.Specific.EnableOpeningSourceGeneratedFilesInWorkspaceExperiment")); - - internal static readonly Option2 EnableOpeningInWorkspaceFeatureFlag = new(FeatureName, nameof(EnableOpeningInWorkspaceFeatureFlag), defaultValue: false, - new FeatureFlagStorageLocation("Roslyn.SourceGeneratorsEnableOpeningInWorkspace")); - - ImmutableArray IOptionProvider.Options => ImmutableArray.Create( - EnableOpeningInWorkspace, - EnableOpeningInWorkspaceFeatureFlag); - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Options() - { - } - } } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs index 6d7a73c7eafbc..21239d12b23ae 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs @@ -122,8 +122,8 @@ public void SetFileScopedNamespaces(bool value) public void SetEnableOpeningSourceGeneratedFilesInWorkspaceExperiment(bool value) { SetOption( - optionName: LanguageServices.Implementation.SourceGeneratedFileManager.Options.EnableOpeningInWorkspace.Name, - feature: LanguageServices.Implementation.SourceGeneratedFileManager.Options.EnableOpeningInWorkspace.Feature, + optionName: WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace.Name, + feature: WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace.Feature, value: value); } diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index 9855802da5742..bc10ca01da249 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -114,10 +114,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options ' Go To Definition BindToOption(NavigateToObjectBrowser, VisualStudioNavigationOptions.NavigateToObjectBrowser, LanguageNames.VisualBasic) - BindToOption(Enable_all_features_in_opened_files_from_source_generators, SourceGeneratedFileManager.Options.EnableOpeningInWorkspace, + BindToOption(Enable_all_features_in_opened_files_from_source_generators, WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace, Function() ' If the option has Not been set by the user, check if the option is enabled from experimentation. - Return optionStore.GetOption(SourceGeneratedFileManager.Options.EnableOpeningInWorkspaceFeatureFlag) + Return optionStore.GetOption(WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag) End Function) ' Regular expressions diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs index d70cef1949bfb..f590cb6cddc2f 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs @@ -26,9 +26,27 @@ internal class WorkspaceConfigurationOptions : IOptionProvider nameof(WorkspaceConfigurationOptions), nameof(DisableProjectCacheService), defaultValue: false, new FeatureFlagStorageLocation("Roslyn.DisableProjectCacheService")); + /// + /// This option allows the user to enable this. We are putting this behind a feature flag for now since we could have extensions + /// surprised by this and we want some time to work through those issues. + /// + internal static readonly Option2 EnableOpeningSourceGeneratedFilesInWorkspace = new("SourceGeneratedFileManager", "EnableOpeningInWorkspace", defaultValue: null, + new RoamingProfileStorageLocation("TextEditor.Roslyn.Specific.EnableOpeningSourceGeneratedFilesInWorkspaceExperiment")); + + internal static readonly Option2 EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag = new("SourceGeneratedFileManager", "EnableOpeningInWorkspaceFeatureFlag", defaultValue: false, + new FeatureFlagStorageLocation("Roslyn.SourceGeneratorsEnableOpeningInWorkspace")); + + internal static bool ShouldConnectSourceGeneratedFilesToWorkspace(OptionSet options) + { + return options.GetOption(EnableOpeningSourceGeneratedFilesInWorkspace) ?? + options.GetOption(EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag); + } + ImmutableArray IOptionProvider.Options { get; } = ImmutableArray.Create( DisableRecoverableTrees, - DisableProjectCacheService); + DisableProjectCacheService, + EnableOpeningSourceGeneratedFilesInWorkspace, + EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]