diff --git a/azure-pipelines-integration.yml b/azure-pipelines-integration.yml index 87a597c974b14..00a7dc616910e 100644 --- a/azure-pipelines-integration.yml +++ b/azure-pipelines-integration.yml @@ -22,14 +22,22 @@ jobs: strategy: maxParallel: 2 matrix: - debug_async: + debug_32: _configuration: Debug - release_async: + _oop64bit: false + debug_64: + _configuration: Debug + _oop64bit: true + release_32: + _configuration: Release + _oop64bit: false + release_64: _configuration: Release + _oop64bit: true timeoutInMinutes: 135 steps: - - script: eng/cibuild.cmd -configuration $(_configuration) -prepareMachine -testVsi + - script: eng/cibuild.cmd -configuration $(_configuration) -prepareMachine -testVsi -oop64bit:$$(_oop64bit) displayName: Build and Test - task: PublishTestResults@2 @@ -38,14 +46,14 @@ jobs: testRunner: XUnit testResultsFiles: $(Build.SourcesDirectory)\artifacts\TestResults\$(_configuration)\*.xml mergeTestResults: true - testRunTitle: '$(System.JobAttempt)-Integration $(_configuration)' + testRunTitle: '$(System.JobAttempt)-Integration $(_configuration) OOP64_$(_oop64bit)' condition: always() - task: PublishBuildArtifacts@1 displayName: Publish Logs inputs: PathtoPublish: '$(Build.SourcesDirectory)\artifacts\log\$(_configuration)' - ArtifactName: '$(System.JobAttempt)-Logs $(_configuration) $(Build.BuildNumber)' + ArtifactName: '$(System.JobAttempt)-Logs $(_configuration) OOP64_$(_oop64bit) $(Build.BuildNumber)' publishLocation: Container continueOnError: true condition: not(succeeded()) @@ -54,7 +62,7 @@ jobs: displayName: Publish Secondary Logs inputs: PathtoPublish: '$(Build.SourcesDirectory)\artifacts\log2\$(_configuration)' - ArtifactName: '$(System.JobAttempt)-Secondary Logs $(_configuration) $(Build.BuildNumber)' + ArtifactName: '$(System.JobAttempt)-Secondary Logs $(_configuration) OOP64_$(_oop64bit) $(Build.BuildNumber)' publishLocation: Container continueOnError: true condition: not(succeeded()) @@ -63,7 +71,7 @@ jobs: displayName: Publish Screenshots inputs: PathtoPublish: '$(Build.SourcesDirectory)\artifacts\bin\Microsoft.VisualStudio.LanguageServices.IntegrationTests\$(_configuration)\net472\xUnitResults' - ArtifactName: '$(System.JobAttempt)-Screenshots $(_configuration) $(Build.BuildNumber)' + ArtifactName: '$(System.JobAttempt)-Screenshots $(_configuration) OOP64_$(_oop64bit) $(Build.BuildNumber)' publishLocation: Container continueOnError: true condition: not(succeeded()) diff --git a/eng/build.ps1 b/eng/build.ps1 index 333a81660516a..41704096180c8 100644 --- a/eng/build.ps1 +++ b/eng/build.ps1 @@ -43,6 +43,7 @@ param ( [switch]$useGlobalNuGetCache = $true, [switch]$warnAsError = $false, [switch]$sourceBuild = $false, + [switch]$oop64bit = $true, # official build settings [string]$officialBuildId = "", @@ -594,6 +595,8 @@ function Setup-IntegrationTestRun() { # Make sure we can capture a screenshot. An exception at this point will fail-fast the build. Capture-Screenshot $screenshotPath } + + $env:ROSLYN_OOP64BIT = "$oop64bit" } function Prepare-TempDir() { diff --git a/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTag.cs b/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTag.cs index 4f43e0b3131fd..9a78e64674674 100644 --- a/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTag.cs +++ b/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTag.cs @@ -13,12 +13,13 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using Microsoft.CodeAnalysis.Text; +using System.Windows.Media; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Editor; @@ -65,11 +66,11 @@ private InlineParameterNameHintsTag(FrameworkElement adornment, ITextView textVi /// The view of the editor /// The span that has the location of the hint /// The symbolkey associated with each parameter - public static InlineParameterNameHintsTag Create(string text, double lineHeight, TextFormattingRunProperties format, - ITextView textView, SnapshotSpan span, SymbolKey key, + public static InlineParameterNameHintsTag Create(string text, TextFormattingRunProperties format, + IWpfTextView textView, SnapshotSpan span, SymbolKey key, InlineParameterNameHintsTaggerProvider taggerProvider) { - return new InlineParameterNameHintsTag(CreateElement(text, lineHeight, format), textView, + return new InlineParameterNameHintsTag(CreateElement(text, textView, format), textView, span, key, taggerProvider); } @@ -119,7 +120,7 @@ public async Task> CreateDescriptionAsync(Cancellati return uiCollection; } - private static FrameworkElement CreateElement(string text, double lineHeight, TextFormattingRunProperties format) + private static FrameworkElement CreateElement(string text, IWpfTextView textView, TextFormattingRunProperties format) { // Constructs the hint block which gets assigned parameter name and fontstyles according to the options // page. Calculates a font size 1/4 smaller than the font size of the rest of the editor @@ -145,13 +146,23 @@ private static FrameworkElement CreateElement(string text, double lineHeight, Te Background = format.BackgroundBrush, Child = block, CornerRadius = new CornerRadius(2), - Height = lineHeight - (0.25 * lineHeight), + Height = textView.LineHeight - (0.25 * textView.LineHeight), HorizontalAlignment = HorizontalAlignment.Center, - Margin = new Thickness(left: 0, top: -0.20 * lineHeight, right: 5, bottom: 0), + Margin = new Thickness(left: 0, top: -0.20 * textView.LineHeight, right: 5, bottom: 0), Padding = new Thickness(1), - VerticalAlignment = VerticalAlignment.Center, + + // Need to set SnapsToDevicePixels and UseLayoutRounding to avoid unnecessary reformatting + SnapsToDevicePixels = textView.VisualElement.SnapsToDevicePixels, + UseLayoutRounding = textView.VisualElement.UseLayoutRounding, + VerticalAlignment = VerticalAlignment.Center }; + // Need to set these properties to avoid unnecessary reformatting because some dependancy properties + // affect layout + TextOptions.SetTextFormattingMode(border, TextOptions.GetTextFormattingMode(textView.VisualElement)); + TextOptions.SetTextHintingMode(border, TextOptions.GetTextHintingMode(textView.VisualElement)); + TextOptions.SetTextRenderingMode(border, TextOptions.GetTextRenderingMode(textView.VisualElement)); + border.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); return border; } diff --git a/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTagger.cs b/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTagger.cs index 774324f75f259..277dba7b609c0 100644 --- a/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTagger.cs +++ b/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTagger.cs @@ -6,10 +6,8 @@ using System; using System.Collections.Generic; -using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting; @@ -48,11 +46,11 @@ internal sealed class InlineParameterNameHintsTagger : ITagger? TagsChanged; - public InlineParameterNameHintsTagger(InlineParameterNameHintsTaggerProvider taggerProvider, ITextView textView, ITextBuffer buffer, ITagAggregator tagAggregator) + public InlineParameterNameHintsTagger(InlineParameterNameHintsTaggerProvider taggerProvider, IWpfTextView textView, ITextBuffer buffer, ITagAggregator tagAggregator) { _cache = new List>(); @@ -128,7 +126,7 @@ public IEnumerable> GetTags(NormalizedSnapshotSp { var dataTagSpan = dataTagSpans[0]; var parameterHintSnapshotSpan = new SnapshotSpan(dataTagSpan.Start, 0); - var parameterHintUITag = InlineParameterNameHintsTag.Create(textTag.ParameterName, _textView.LineHeight, + var parameterHintUITag = InlineParameterNameHintsTag.Create(textTag.ParameterName, Format, _textView, dataTagSpan, textTag.ParameterSymbolKey, _inlineParameterNameHintsTaggerProvider); _cache.Add(new TagSpan(parameterHintSnapshotSpan, parameterHintUITag)); diff --git a/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTaggerProvider.cs index 5902c9c18ff87..fc58cd076cd04 100644 --- a/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineParameterNameHints/InlineParameterNameHintsTaggerProvider.cs @@ -60,7 +60,7 @@ public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where } var tagAggregator = _viewTagAggregatorFactoryService.CreateTagAggregator(textView); - return new InlineParameterNameHintsTagger(this, textView, buffer, tagAggregator) as ITagger; + return new InlineParameterNameHintsTagger(this, (IWpfTextView)textView, buffer, tagAggregator) as ITagger; } } } diff --git a/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs b/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs new file mode 100644 index 0000000000000..42ba15597de0a --- /dev/null +++ b/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer.cs @@ -0,0 +1,190 @@ +// 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 enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.DesignerAttribute; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.SolutionCrawler; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.DesignerAttribute +{ + internal abstract partial class AbstractDesignerAttributeIncrementalAnalyzer : IncrementalAnalyzerBase + { + private const string DataKey = "DesignerAttributeData"; + + private readonly IPersistentStorageService _storageService; + + protected AbstractDesignerAttributeIncrementalAnalyzer(Workspace workspace) + { + _storageService = workspace.Services.GetRequiredService(); + } + + protected abstract Task ReportProjectRemovedAsync(ProjectId projectId, CancellationToken cancellationToken); + + protected abstract Task ReportDesignerAttributeDataAsync(List data, CancellationToken cancellationToken); + + public override Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) + => ReportProjectRemovedAsync(projectId, cancellationToken); + + public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + => AnalyzeProjectAsync(project, specificDocument: null, cancellationToken); + + public override Task AnalyzeDocumentAsync(Document document, SyntaxNode? body, InvocationReasons reasons, CancellationToken cancellationToken) + { + // don't need to reanalyze file if just a method body was edited. That can't + // affect designer attributes. + if (body != null) + return Task.CompletedTask; + + // When we register our analyzer we will get called into for every document to + // 'reanalyze' them all. Ignore those as we would prefer to analyze the project + // en-mass. + if (reasons.Contains(PredefinedInvocationReasons.Reanalyze)) + return Task.CompletedTask; + + return AnalyzeProjectAsync(document.Project, document, cancellationToken); + } + + private async Task AnalyzeProjectAsync(Project project, Document? specificDocument, CancellationToken cancellationToken) + { + if (!project.SupportsCompilation) + return; + + // We need to reanalyze the project whenever it (or any of its dependencies) have + // changed. We need to know about dependencies since if a downstream project adds the + // DesignerCategory attribute to a class, that can affect us when we examine the classes + // in this project. + var projectVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + + var latestInfos = await ComputeLatestInfosAsync( + project, projectVersion, specificDocument, cancellationToken).ConfigureAwait(false); + + // Now get all the values that actually changed and notify VS about them. We don't need + // to tell it about the ones that didn't change since that will have no effect on the + // user experience. + // + // ! is safe here as `i.changed` implies `i.info` is non-null. + var changedInfos = latestInfos.Where(i => i.changed).Select(i => i.data!.Value).ToList(); + if (changedInfos.Count > 0) + { + await ReportDesignerAttributeDataAsync(changedInfos, cancellationToken).ConfigureAwait(false); + } + + // now that we've notified VS, persist all the infos we have (changed or otherwise) back + // to disk. We want to do this even when the data is unchanged so that our version + // stamps will be correct for the next time we come around to analyze this project. + // + // Note: we have a potential race condition here. Specifically, for simplicity, the VS + // side will return immediately, without actually notifying the project system. That + // means that we could persist the data to local storage that isn't in sync with what + // the project system knows about. i.e. if VS is closed or crashes before that + // information is persisted, then these two systems will be in disagreement. this is + // believed to not be a big issue given how small a time window this would be and how + // easy it would be to get out of that state (just edit the file). + + await PersistLatestInfosAsync(project.Solution, projectVersion, latestInfos, cancellationToken).ConfigureAwait(false); + } + + private async Task PersistLatestInfosAsync( + Solution solution, VersionStamp projectVersion, (Document, DesignerAttributeData? daa, bool changed)[] latestInfos, CancellationToken cancellationToken) + { + using var storage = _storageService.GetStorage(solution); + + foreach (var (doc, info, _) in latestInfos) + { + // Skip documents that didn't change contents/version at all. No point in writing + // back out the exact same data as before. + if (info == null) + continue; + + using var memoryStream = new MemoryStream(); + using var writer = new ObjectWriter(memoryStream); + + PersistInfoTo(writer, info.Value, projectVersion); + + memoryStream.Position = 0; + await storage.WriteStreamAsync( + doc, DataKey, memoryStream, cancellationToken).ConfigureAwait(false); + } + } + + private async Task<(Document, DesignerAttributeData? data, bool changed)[]> ComputeLatestInfosAsync( + Project project, VersionStamp projectVersion, + Document? specificDocument, CancellationToken cancellationToken) + { + using var storage = _storageService.GetStorage(project.Solution); + + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + var designerCategoryType = compilation.DesignerCategoryAttributeType(); + + using var _ = ArrayBuilder>.GetInstance(out var tasks); + foreach (var document in project.Documents) + { + // If we're only analyzing a specific document, then skip the rest. + if (specificDocument != null && document != specificDocument) + continue; + + tasks.Add(ComputeDesignerAttributeDataAsync( + storage, projectVersion, designerCategoryType, document, cancellationToken)); + } + + return await Task.WhenAll(tasks).ConfigureAwait(false); + } + + private static async Task<(Document, DesignerAttributeData?, bool changed)> ComputeDesignerAttributeDataAsync( + IPersistentStorage storage, VersionStamp projectVersion, INamedTypeSymbol? designerCategoryType, + Document document, CancellationToken cancellationToken) + { + try + { + // If we don't have a path for this document, we cant proceed with it. + // We need that path to inform the project system which file we're referring to. + if (document.FilePath == null) + return default; + + // First check and see if we have stored information for this doc and if that + // information is up to date. + using var stream = await storage.ReadStreamAsync(document, DataKey, cancellationToken).ConfigureAwait(false); + using var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken); + var persisted = TryReadPersistedInfo(reader); + if (persisted.category != null && persisted.projectVersion == projectVersion) + { + // We were able to read out the old data, and it matches our current project + // version. Just return back that nothing changed here. We won't tell VS about + // this, and we won't re-persist this later. + return default; + } + + // We either haven't computed the designer info, or our data was out of date. We need + // So recompute here. Figure out what the current category is, and if that's different + // from what we previously stored. + var category = await DesignerAttributeHelpers.ComputeDesignerAttributeCategoryAsync( + designerCategoryType, document, cancellationToken).ConfigureAwait(false); + var data = new DesignerAttributeData + { + Category = category, + DocumentId = document.Id, + FilePath = document.FilePath, + }; + + return (document, data, changed: category != persisted.category); + } + catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e)) + { + return default; + } + } + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer_Serialization.cs b/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer_Serialization.cs similarity index 94% rename from src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer_Serialization.cs rename to src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer_Serialization.cs index 29d4b94947c62..9155f0a20cfe7 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer_Serialization.cs +++ b/src/Features/Core/Portable/DesignerAttribute/AbstractDesignerAttributeIncrementalAnalyzer_Serialization.cs @@ -9,9 +9,9 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Remote +namespace Microsoft.CodeAnalysis.DesignerAttribute { - internal partial class RemoteDesignerAttributeIncrementalAnalyzer + internal partial class AbstractDesignerAttributeIncrementalAnalyzer { /// /// Our current serialization format. Increment whenever it changes so that we don't read diff --git a/src/Features/Core/Portable/TodoComments/AbstractTodoCommentsIncrementalAnalyzer.cs b/src/Features/Core/Portable/TodoComments/AbstractTodoCommentsIncrementalAnalyzer.cs new file mode 100644 index 0000000000000..a91157e8b5b3b --- /dev/null +++ b/src/Features/Core/Portable/TodoComments/AbstractTodoCommentsIncrementalAnalyzer.cs @@ -0,0 +1,89 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Implementation.TodoComments; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.SolutionCrawler; + +namespace Microsoft.CodeAnalysis.TodoComments +{ + internal abstract partial class AbstractTodoCommentsIncrementalAnalyzer : IncrementalAnalyzerBase + { + private readonly object _gate = new object(); + private string? _lastOptionText = null; + private ImmutableArray _lastDescriptors = default; + + protected AbstractTodoCommentsIncrementalAnalyzer() + { + } + + protected abstract Task ReportTodoCommentDataAsync(DocumentId documentId, ImmutableArray data, CancellationToken cancellationToken); + + public override bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) + => e.Option == TodoCommentOptions.TokenList; + + public override Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) + { + // Just report this back as there being no more comments for this document. + return ReportTodoCommentDataAsync(documentId, ImmutableArray.Empty, cancellationToken); + } + + private ImmutableArray GetTodoCommentDescriptors(Document document) + { + var optionText = document.Project.Solution.Options.GetOption(TodoCommentOptions.TokenList); + + lock (_gate) + { + if (optionText != _lastOptionText) + { + _lastDescriptors = TodoCommentDescriptor.Parse(optionText); + _lastOptionText = optionText; + } + + return _lastDescriptors; + } + } + + public override async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) + { + var todoCommentService = document.GetLanguageService(); + if (todoCommentService == null) + return; + + var descriptors = GetTodoCommentDescriptors(document); + + // We're out of date. Recompute this info. + var todoComments = await todoCommentService.GetTodoCommentsAsync( + document, descriptors, cancellationToken).ConfigureAwait(false); + + // Convert the roslyn-level results to the more VS oriented line/col data. + using var _ = ArrayBuilder.GetInstance(out var converted); + await ConvertAsync( + document, todoComments, converted, cancellationToken).ConfigureAwait(false); + + // Now inform VS about this new information + await ReportTodoCommentDataAsync(document.Id, converted.ToImmutable(), cancellationToken).ConfigureAwait(false); + } + + private static async Task ConvertAsync( + Document document, + ImmutableArray todoComments, + ArrayBuilder converted, + CancellationToken cancellationToken) + { + var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + + foreach (var comment in todoComments) + converted.Add(comment.CreateSerializableData(document, sourceText, syntaxTree)); + } + } +} diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml index 6d753064edced..06bee807237e7 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml @@ -29,6 +29,8 @@ + ServicesVSResources.Enable_navigation_to_decompiled_sources; + public static string Option_use_64bit_analysis_process + => ServicesVSResources.Use_64_bit_process_for_code_analysis_requires_restart; + public static string Option_Display_inline_parameter_name_hints => ServicesVSResources.Display_inline_parameter_name_hints; diff --git a/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostOptionsAccessor.cs b/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostOptionsAccessor.cs index 8ca0d49008af3..84a7c1bbc2591 100644 --- a/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostOptionsAccessor.cs +++ b/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostOptionsAccessor.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. +using System.Linq; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; @@ -9,10 +10,8 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api { internal static class UnitTestingRemoteHostOptionsAccessor { - private const string LocalRegistryPath = @"Roslyn\Internal\RemoteServices\"; - public static Option OOP64Bit => new Option( - nameof(RemoteHostOptions), nameof(OOP64Bit), defaultValue: false, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(OOP64Bit))); + RemoteHostOptions.OOP64Bit.Feature, RemoteHostOptions.OOP64Bit.Name, defaultValue: RemoteHostOptions.OOP64Bit.DefaultValue, + storageLocations: RemoteHostOptions.OOP64Bit.StorageLocations.ToArray()); } } diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/InProcDesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/InProcDesignerAttributeIncrementalAnalyzer.cs new file mode 100644 index 0000000000000..7ae9157b36202 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/InProcDesignerAttributeIncrementalAnalyzer.cs @@ -0,0 +1,30 @@ +// 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 System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.DesignerAttribute; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute +{ + internal sealed class InProcDesignerAttributeIncrementalAnalyzer : AbstractDesignerAttributeIncrementalAnalyzer + { + private readonly IDesignerAttributeListener _listener; + + public InProcDesignerAttributeIncrementalAnalyzer(Workspace workspace, IDesignerAttributeListener listener) + : base(workspace) + { + _listener = listener; + } + + protected override Task ReportProjectRemovedAsync(ProjectId projectId, CancellationToken cancellationToken) + => _listener.OnProjectRemovedAsync(projectId, cancellationToken); + + protected override Task ReportDesignerAttributeDataAsync(List data, CancellationToken cancellationToken) + => _listener.ReportDesignerAttributeDataAsync(data.ToImmutableArray(), cancellationToken); + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/InProcDesignerAttributeIncrementalAnalyzerProvider.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/InProcDesignerAttributeIncrementalAnalyzerProvider.cs new file mode 100644 index 0000000000000..7ff4fb0884adc --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/InProcDesignerAttributeIncrementalAnalyzerProvider.cs @@ -0,0 +1,28 @@ +// 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; +using Microsoft.CodeAnalysis.DesignerAttribute; +using Microsoft.CodeAnalysis.SolutionCrawler; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute +{ + /// 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 InProcDesignerAttributeIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider + { + private readonly IDesignerAttributeListener _listener; + + public InProcDesignerAttributeIncrementalAnalyzerProvider(IDesignerAttributeListener listener) + { + _listener = listener; + } + + public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) + => new InProcDesignerAttributeIncrementalAnalyzer(workspace, _listener); + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs index 9c29ba92c59d9..1e28a1f8879c7 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs @@ -21,6 +21,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.VisualStudio.Designer.Interfaces; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell.Interop; @@ -114,7 +115,10 @@ private async Task StartWorkerAsync(CancellationToken cancellationToken) var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) + { + StartScanningForDesignerAttributesInCurrentProcess(cancellationToken); return; + } // Pass ourselves in as the callback target for the OOP service. As it discovers // designer attributes it will call back into us to notify VS about it. @@ -130,6 +134,19 @@ await _connection.RunRemoteAsync( cancellationToken).ConfigureAwait(false); } + public void StartScanningForDesignerAttributesInCurrentProcess(CancellationToken cancellation) + { + var registrationService = _workspace.Services.GetRequiredService(); + var analyzerProvider = new InProcDesignerAttributeIncrementalAnalyzerProvider(this); + + registrationService.AddAnalyzerProvider( + analyzerProvider, + new IncrementalAnalyzerProviderMetadata( + nameof(InProcDesignerAttributeIncrementalAnalyzerProvider), + highPriorityForActiveFile: false, + workspaceKinds: WorkspaceKind.Host)); + } + private async Task NotifyProjectSystemAsync( ImmutableArray data, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/DefaultRemoteHostClientProvider.cs b/src/VisualStudio/Core/Def/Implementation/Remote/DefaultRemoteHostClientProvider.cs new file mode 100644 index 0000000000000..662925a48762b --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/Remote/DefaultRemoteHostClientProvider.cs @@ -0,0 +1,26 @@ +// 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 enable + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Remote; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.Remote +{ + internal sealed class DefaultRemoteHostClientProvider : IRemoteHostClientProvider + { + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + public DefaultRemoteHostClientProvider() + { + } + + public Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken) + => SpecializedTasks.Null(); + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs b/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs index 68329e7172ff0..8eb21289dbfbb 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs @@ -28,14 +28,24 @@ public Factory() { } + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new VisualStudioRemoteHostClientProvider(workspaceServices); + { + if (!RemoteHostOptions.IsUsingServiceHubOutOfProcess(workspaceServices) + || workspaceServices.Workspace is not VisualStudioWorkspace) + { + // Run code in the current process + return new DefaultRemoteHostClientProvider(); + } + + return new VisualStudioRemoteHostClientProvider(workspaceServices); + } } private readonly HostWorkspaceServices _services; private readonly AsyncLazy _lazyClient; - public VisualStudioRemoteHostClientProvider(HostWorkspaceServices services) + private VisualStudioRemoteHostClientProvider(HostWorkspaceServices services) { _services = services; _lazyClient = new AsyncLazy(CreateHostClientAsync, cacheResult: true); @@ -60,6 +70,6 @@ await client.RunRemoteAsync( } public Task TryGetRemoteHostClientAsync(CancellationToken cancellationToken) - => (_services.Workspace is VisualStudioWorkspace) ? _lazyClient.GetValueAsync(cancellationToken).AsNullable() : SpecializedTasks.Null(); + => _lazyClient.GetValueAsync(cancellationToken).AsNullable(); } } diff --git a/src/VisualStudio/Core/Def/Implementation/TodoComments/InProcTodoCommentsIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/TodoComments/InProcTodoCommentsIncrementalAnalyzer.cs new file mode 100644 index 0000000000000..ffced79472572 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/TodoComments/InProcTodoCommentsIncrementalAnalyzer.cs @@ -0,0 +1,25 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.TodoComments; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments +{ + internal sealed class InProcTodoCommentsIncrementalAnalyzer : AbstractTodoCommentsIncrementalAnalyzer + { + private readonly ITodoCommentsListener _listener; + + public InProcTodoCommentsIncrementalAnalyzer(ITodoCommentsListener listener) + => _listener = listener; + + protected override Task ReportTodoCommentDataAsync(DocumentId documentId, ImmutableArray data, CancellationToken cancellationToken) + => _listener.ReportTodoCommentDataAsync(documentId, data, cancellationToken); + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/TodoComments/InProcTodoCommentsIncrementalAnalyzerProvider.cs b/src/VisualStudio/Core/Def/Implementation/TodoComments/InProcTodoCommentsIncrementalAnalyzerProvider.cs new file mode 100644 index 0000000000000..d866a9c0ec915 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/TodoComments/InProcTodoCommentsIncrementalAnalyzerProvider.cs @@ -0,0 +1,28 @@ +// 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 enable + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.SolutionCrawler; +using Microsoft.CodeAnalysis.TodoComments; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.TodoComments +{ + /// 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 InProcTodoCommentsIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider + { + private readonly ITodoCommentsListener _listener; + + public InProcTodoCommentsIncrementalAnalyzerProvider(ITodoCommentsListener listener) + => _listener = listener; + + public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) + => new InProcTodoCommentsIncrementalAnalyzer(_listener); + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs b/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs index e83f57821ea3a..39fcc7156d0b0 100644 --- a/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs +++ b/src/VisualStudio/Core/Def/Implementation/TodoComments/VisualStudioTodoCommentsService.cs @@ -21,6 +21,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.TodoComments; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.VSTypeScript.Api; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; @@ -101,7 +102,10 @@ private async Task StartWorkerAsync(CancellationToken cancellationToken) var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) + { + ComputeTodoCommentsInCurrentProcess(cancellationToken); return; + } // Pass ourselves in as the callback target for the OOP service. As it discovers // todo comments it will call back into us to notify VS about it. @@ -120,6 +124,19 @@ await _connection.RunRemoteAsync( cancellationToken).ConfigureAwait(false); } + private void ComputeTodoCommentsInCurrentProcess(CancellationToken cancellationToken) + { + var registrationService = _workspace.Services.GetRequiredService(); + var analyzerProvider = new InProcTodoCommentsIncrementalAnalyzerProvider(this); + + registrationService.AddAnalyzerProvider( + analyzerProvider, + new IncrementalAnalyzerProviderMetadata( + nameof(InProcTodoCommentsIncrementalAnalyzerProvider), + highPriorityForActiveFile: false, + workspaceKinds: WorkspaceKind.Host)); + } + private Task ProcessTodoCommentInfosAsync( ImmutableArray docAndCommentsArray, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index 2d4efb29171ac..e871d21ef6363 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1532,4 +1532,7 @@ I agree to all of the foregoing: 64-bit + + Use 64-bit process for code analysis (requires restart) + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index 4116d596814a6..e85724a5036b2 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -802,6 +802,11 @@ Aktualizuje se závažnost. + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Pro výrazy lambda používat text výrazu diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 7211ef460a88d..c225c18360fd3 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -802,6 +802,11 @@ Der Schweregrad wird aktualisiert. + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Ausdruckskörper für Lambdaausdrücke verwenden diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index b21cee3da316d..1e2c843fb3a3e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -802,6 +802,11 @@ Actualizando la gravedad + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Usar cuerpo de expresión para lambdas diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index d195a28d98feb..6e14c2be671f0 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -802,6 +802,11 @@ Mise à jour de la gravité + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Utiliser un corps d'expression pour les expressions lambda diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index f02afccd4ccec..219281382d10d 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -802,6 +802,11 @@ Aggiornamento della gravità + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Usa il corpo dell'espressione per le espressioni lambda diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index dc71d2c7b1c06..d260d62ab7f64 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -802,6 +802,11 @@ 重要度を更新しています + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas ラムダに式本体を使用します diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 90f7e75c5fcbc..a06da8b676ded 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -802,6 +802,11 @@ 심각도를 업데이트하는 중 + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas 람다에 식 본문 사용 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 991ce5c5b2cc1..20a0c90f2566b 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -802,6 +802,11 @@ Aktualizowanie ważności + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Użyj treści wyrażenia dla wyrażeń lambda diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index ca479e03e6cb3..76ec37967a169 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -802,6 +802,11 @@ Atualizando a severidade + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Usar o corpo da expressão para lambdas diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 349118bac0964..d420d0c031b91 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -802,6 +802,11 @@ Обновление уровня серьезности + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Использовать тело выражения для лямбда-выражений diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 7ad37e8615ca1..6449db3897263 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -802,6 +802,11 @@ Önem derecesi güncelleştiriliyor + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas Lambdalar için ifade gövdesi kullan diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index b916017b03dd5..b0c7631b5e3ca 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -802,6 +802,11 @@ 正在更新严重性 + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas 使用 lambdas 的表达式主体 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index 1aef149101426..42ac7c3f4dc4f 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -802,6 +802,11 @@ 正在更新嚴重性 + + Use 64-bit process for code analysis (requires restart) + Use 64-bit process for code analysis (requires restart) + + Use expression body for lambdas 使用 lambda 的運算式主體 diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs index 8a83992105dd8..0dea824e6bc37 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/VisualStudioInstanceFactory.cs @@ -340,6 +340,12 @@ private static Process StartNewVisualStudioProcess(string installationPath, int // custom handler or fail silently and continue testing. Process.Start(CreateSilentStartInfo(vsRegEditExeFile, $"set \"{installationPath}\" {Settings.Default.VsRootSuffix} HKCU \"Text Editor\" \"Report Exceptions\" dword 0")).WaitForExit(); + // Configure RemoteHostOptions.OOP64Bit for testing + if (string.Equals(Environment.GetEnvironmentVariable("ROSLYN_OOP64BIT"), "false", StringComparison.OrdinalIgnoreCase)) + { + Process.Start(CreateSilentStartInfo(vsRegEditExeFile, $"set \"{installationPath}\" {Settings.Default.VsRootSuffix} HKCU \"Roslyn\\Internal\\OnOff\\Features\" OOP64Bit dword 0")).WaitForExit(); + } + _firstLaunch = false; } diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml index 25574137a8311..9e6f8945a85c4 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml @@ -28,6 +28,8 @@ x:Name="Background_analysis_scope_full_solution" Content="{x:Static local:AdvancedOptionPageStrings.Option_Background_Analysis_Scope_Full_Solution}"/> + + + diff --git a/src/Workspaces/Remote/Core/RemoteHostOptions.cs b/src/Workspaces/Remote/Core/RemoteHostOptions.cs index 445af2ed5fa8e..1eb6883bd5d5d 100644 --- a/src/Workspaces/Remote/Core/RemoteHostOptions.cs +++ b/src/Workspaces/Remote/Core/RemoteHostOptions.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Immutable; using System.Composition; -using Microsoft.CodeAnalysis.Experiments; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; @@ -30,13 +29,40 @@ internal static class RemoteHostOptions storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(SolutionChecksumMonitorBackOffTimeSpanInMS))); // use 64bit OOP - public static readonly Option OOP64Bit = new Option( - FeatureName, nameof(OOP64Bit), defaultValue: false, + public static readonly Option2 OOP64Bit = new Option2( + FeatureName, nameof(OOP64Bit), defaultValue: true, storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(OOP64Bit))); + // Override 64-bit OOP option to force use of a 32-bit process. This option exists as a registry-based + // workaround for cases where the new 64-bit mode fails and 32-bit in-process fails to provide a viable + // fallback. + public static readonly Option2 OOP32BitOverride = new Option2( + FeatureName, nameof(OOP32BitOverride), defaultValue: false, + storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(OOP32BitOverride))); + public static bool IsServiceHubProcess64Bit(HostWorkspaceServices services) - => services.GetRequiredService().GetOption(OOP64Bit) || - services.GetRequiredService().IsExperimentEnabled(WellKnownExperimentNames.RoslynOOP64bit); + => IsUsingServiceHubOutOfProcess(services) && !services.GetRequiredService().GetOption(OOP32BitOverride); + + /// + /// Determines whether ServiceHub out-of-process execution is enabled for Roslyn. + /// + public static bool IsUsingServiceHubOutOfProcess(HostWorkspaceServices services) + { + var optionService = services.GetRequiredService(); + if (Environment.Is64BitOperatingSystem && optionService.GetOption(OOP64Bit)) + { + // OOP64Bit is set and supported + return true; + } + + if (optionService.GetOption(OOP32BitOverride)) + { + // Hidden fallback to 32-bit OOP is set + return true; + } + + return false; + } } [ExportOptionProvider, Shared] diff --git a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer.cs b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer.cs index c775ca3a2d0d7..f98a5fde47d91 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DesignerAttribute/RemoteDesignerAttributeIncrementalAnalyzer.cs @@ -4,39 +4,27 @@ #nullable enable -using System; -using System.IO; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DesignerAttribute; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote { - internal sealed partial class RemoteDesignerAttributeIncrementalAnalyzer : IncrementalAnalyzerBase + internal sealed class RemoteDesignerAttributeIncrementalAnalyzer : AbstractDesignerAttributeIncrementalAnalyzer { - private const string DataKey = "DesignerAttributeData"; - /// /// Channel back to VS to inform it of the designer attributes we discover. /// private readonly RemoteEndPoint _endPoint; - private readonly IPersistentStorageService _storageService; - public RemoteDesignerAttributeIncrementalAnalyzer(Workspace workspace, RemoteEndPoint endPoint) + : base(workspace) { _endPoint = endPoint; - _storageService = workspace.Services.GetRequiredService(); } - public override Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellationToken) + protected override Task ReportProjectRemovedAsync(ProjectId projectId, CancellationToken cancellationToken) { return _endPoint.InvokeAsync( nameof(IDesignerAttributeListener.OnProjectRemovedAsync), @@ -44,156 +32,12 @@ public override Task RemoveProjectAsync(ProjectId projectId, CancellationToken c cancellationToken); } - public override Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) - => AnalyzeProjectAsync(project, specificDocument: null, cancellationToken); - - public override Task AnalyzeDocumentAsync(Document document, SyntaxNode? body, InvocationReasons reasons, CancellationToken cancellationToken) - { - // don't need to reanalyze file if just a method body was edited. That can't - // affect designer attributes. - if (body != null) - return Task.CompletedTask; - - // When we register our analyzer we will get called into for every document to - // 'reanalyze' them all. Ignore those as we would prefer to analyze the project - // en-mass. - if (reasons.Contains(PredefinedInvocationReasons.Reanalyze)) - return Task.CompletedTask; - - return AnalyzeProjectAsync(document.Project, document, cancellationToken); - } - - private async Task AnalyzeProjectAsync(Project project, Document? specificDocument, CancellationToken cancellationToken) - { - if (!project.SupportsCompilation) - return; - - // We need to reanalyze the project whenever it (or any of its dependencies) have - // changed. We need to know about dependencies since if a downstream project adds the - // DesignerCategory attribute to a class, that can affect us when we examine the classes - // in this project. - var projectVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - - var latestInfos = await ComputeLatestInfosAsync( - project, projectVersion, specificDocument, cancellationToken).ConfigureAwait(false); - - // Now get all the values that actually changed and notify VS about them. We don't need - // to tell it about the ones that didn't change since that will have no effect on the - // user experience. - // - // ! is safe here as `i.changed` implies `i.info` is non-null. - var changedInfos = latestInfos.Where(i => i.changed).Select(i => i.data!.Value).ToList(); - if (changedInfos.Count > 0) - { - await _endPoint.InvokeAsync( - nameof(IDesignerAttributeListener.ReportDesignerAttributeDataAsync), - new object[] { changedInfos }, - cancellationToken).ConfigureAwait(false); - } - - // now that we've notified VS, persist all the infos we have (changed or otherwise) back - // to disk. We want to do this even when the data is unchanged so that our version - // stamps will be correct for the next time we come around to analyze this project. - // - // Note: we have a potential race condition here. Specifically, for simplicity, the VS - // side will return immediately, without actually notifying the project system. That - // means that we could persist the data to local storage that isn't in sync with what - // the project system knows about. i.e. if VS is closed or crashes before that - // information is persisted, then these two systems will be in disagreement. this is - // believed to not be a big issue given how small a time window this would be and how - // easy it would be to get out of that state (just edit the file). - - await PersistLatestInfosAsync(project.Solution, projectVersion, latestInfos, cancellationToken).ConfigureAwait(false); - } - - private async Task PersistLatestInfosAsync( - Solution solution, VersionStamp projectVersion, (Document, DesignerAttributeData? daa, bool changed)[] latestInfos, CancellationToken cancellationToken) + protected override Task ReportDesignerAttributeDataAsync(List data, CancellationToken cancellationToken) { - using var storage = _storageService.GetStorage(solution); - - foreach (var (doc, info, _) in latestInfos) - { - // Skip documents that didn't change contents/version at all. No point in writing - // back out the exact same data as before. - if (info == null) - continue; - - using var memoryStream = new MemoryStream(); - using var writer = new ObjectWriter(memoryStream); - - PersistInfoTo(writer, info.Value, projectVersion); - - memoryStream.Position = 0; - await storage.WriteStreamAsync( - doc, DataKey, memoryStream, cancellationToken).ConfigureAwait(false); - } - } - - private async Task<(Document, DesignerAttributeData? data, bool changed)[]> ComputeLatestInfosAsync( - Project project, VersionStamp projectVersion, - Document? specificDocument, CancellationToken cancellationToken) - { - using var storage = _storageService.GetStorage(project.Solution); - - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - var designerCategoryType = compilation.DesignerCategoryAttributeType(); - - using var _ = ArrayBuilder>.GetInstance(out var tasks); - foreach (var document in project.Documents) - { - // If we're only analyzing a specific document, then skip the rest. - if (specificDocument != null && document != specificDocument) - continue; - - tasks.Add(ComputeDesignerAttributeDataAsync( - storage, projectVersion, designerCategoryType, document, cancellationToken)); - } - - return await Task.WhenAll(tasks).ConfigureAwait(false); - } - - private static async Task<(Document, DesignerAttributeData?, bool changed)> ComputeDesignerAttributeDataAsync( - IPersistentStorage storage, VersionStamp projectVersion, INamedTypeSymbol? designerCategoryType, - Document document, CancellationToken cancellationToken) - { - try - { - // If we don't have a path for this document, we cant proceed with it. - // We need that path to inform the project system which file we're referring to. - if (document.FilePath == null) - return default; - - // First check and see if we have stored information for this doc and if that - // information is up to date. - using var stream = await storage.ReadStreamAsync(document, DataKey, cancellationToken).ConfigureAwait(false); - using var reader = ObjectReader.TryGetReader(stream, cancellationToken: cancellationToken); - var persisted = TryReadPersistedInfo(reader); - if (persisted.category != null && persisted.projectVersion == projectVersion) - { - // We were able to read out the old data, and it matches our current project - // version. Just return back that nothing changed here. We won't tell VS about - // this, and we won't re-persist this later. - return default; - } - - // We either haven't computed the designer info, or our data was out of date. We need - // So recompute here. Figure out what the current category is, and if that's different - // from what we previously stored. - var category = await DesignerAttributeHelpers.ComputeDesignerAttributeCategoryAsync( - designerCategoryType, document, cancellationToken).ConfigureAwait(false); - var data = new DesignerAttributeData - { - Category = category, - DocumentId = document.Id, - FilePath = document.FilePath, - }; - - return (document, data, changed: category != persisted.category); - } - catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e)) - { - return default; - } + return _endPoint.InvokeAsync( + nameof(IDesignerAttributeListener.ReportDesignerAttributeDataAsync), + new object[] { data }, + cancellationToken); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzer.cs b/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzer.cs index faf9f9f6b241c..3b5caf13be6b6 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzer.cs @@ -4,99 +4,29 @@ #nullable enable -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.Implementation.TodoComments; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.TodoComments; namespace Microsoft.CodeAnalysis.Remote { - internal partial class RemoteTodoCommentsIncrementalAnalyzer : IncrementalAnalyzerBase + internal sealed class RemoteTodoCommentsIncrementalAnalyzer : AbstractTodoCommentsIncrementalAnalyzer { - private const string DataKey = "TodoComment"; - /// /// Channel back to VS to inform it of the designer attributes we discover. /// private readonly RemoteEndPoint _endPoint; - private readonly object _gate = new object(); - private string? _lastOptionText = null; - private ImmutableArray _lastDescriptors = default; - public RemoteTodoCommentsIncrementalAnalyzer(RemoteEndPoint endPoint) => _endPoint = endPoint; - public override bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) - => e.Option == TodoCommentOptions.TokenList; - - public override Task RemoveDocumentAsync(DocumentId documentId, CancellationToken cancellationToken) + protected override Task ReportTodoCommentDataAsync(DocumentId documentId, ImmutableArray data, CancellationToken cancellationToken) { - // Just report this back as there being no more comments for this document. return _endPoint.InvokeAsync( nameof(ITodoCommentsListener.ReportTodoCommentDataAsync), - new object[] { documentId, Array.Empty() }, + new object[] { documentId, data }, cancellationToken); } - - private ImmutableArray GetTodoCommentDescriptors(Document document) - { - var optionText = document.Project.Solution.Options.GetOption(TodoCommentOptions.TokenList); - - lock (_gate) - { - if (optionText != _lastOptionText) - { - _lastDescriptors = TodoCommentDescriptor.Parse(optionText); - _lastOptionText = optionText; - } - - return _lastDescriptors; - } - } - - public override async Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) - { - var todoCommentService = document.GetLanguageService(); - if (todoCommentService == null) - return; - - var descriptors = GetTodoCommentDescriptors(document); - - // We're out of date. Recompute this info. - var todoComments = await todoCommentService.GetTodoCommentsAsync( - document, descriptors, cancellationToken).ConfigureAwait(false); - - // Convert the roslyn-level results to the more VS oriented line/col data. - using var _ = ArrayBuilder.GetInstance(out var converted); - await ConvertAsync( - document, todoComments, converted, cancellationToken).ConfigureAwait(false); - - // Now inform VS about this new information - await _endPoint.InvokeAsync( - nameof(ITodoCommentsListener.ReportTodoCommentDataAsync), - new object[] { document.Id, converted }, - cancellationToken).ConfigureAwait(false); - } - - private static async Task ConvertAsync( - Document document, - ImmutableArray todoComments, - ArrayBuilder converted, - CancellationToken cancellationToken) - { - var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - - foreach (var comment in todoComments) - converted.Add(comment.CreateSerializableData(document, sourceText, syntaxTree)); - } } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzerProvider.cs b/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzerProvider.cs index c8a6a72cc7fec..c75616fb64c01 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzerProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/TodoComments/RemoteTodoCommentsIncrementalAnalyzerProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Remote /// and then calls into OOP to tell it to start analyzing the solution. At that point we'll get /// created and added to the solution crawler. /// - internal class RemoteTodoCommentsIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider + internal sealed class RemoteTodoCommentsIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider { private readonly RemoteEndPoint _endPoint;